Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-11-17 00:12:37 +00:00
parent f9e0126cad
commit 049fd8333a
43 changed files with 362 additions and 189 deletions

View File

@ -39,8 +39,8 @@ export default {
assignSelf() {
this.$emit('assign-self');
},
toggleAttentionRequired(data) {
this.$emit('toggle-attention-required', data);
toggleAttentionRequested(data) {
this.$emit('toggle-attention-requested', data);
},
},
};
@ -65,7 +65,7 @@ export default {
v-else
:users="sortedAssigness"
:issuable-type="issuableType"
@toggle-attention-required="toggleAttentionRequired"
@toggle-attention-requested="toggleAttentionRequested"
/>
</div>
</div>

View File

@ -33,8 +33,8 @@ export default {
},
},
methods: {
toggleAttentionRequired(data) {
this.$emit('toggle-attention-required', data);
toggleAttentionRequested(data) {
this.$emit('toggle-attention-requested', data);
},
},
};
@ -66,7 +66,7 @@ export default {
:users="users"
:issuable-type="issuableType"
class="gl-text-gray-800 gl-mt-2 hide-collapsed"
@toggle-attention-required="toggleAttentionRequired"
@toggle-attention-requested="toggleAttentionRequested"
/>
</div>
</template>

View File

@ -125,8 +125,8 @@ export default {
availability: this.assigneeAvailabilityStatus[username] || '',
}));
},
toggleAttentionRequired(data) {
this.mediator.toggleAttentionRequired('assignee', data);
toggleAttentionRequested(data) {
this.mediator.toggleAttentionRequested('assignee', data);
},
},
};
@ -155,7 +155,7 @@ export default {
:editable="store.editable"
:issuable-type="issuableType"
@assign-self="assignSelf"
@toggle-attention-required="toggleAttentionRequired"
@toggle-attention-requested="toggleAttentionRequested"
/>
</div>
</template>

View File

@ -2,7 +2,7 @@
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { IssuableType } from '~/issue_show/constants';
import { __, sprintf } from '~/locale';
import AttentionRequiredToggle from '../attention_required_toggle.vue';
import AttentionRequestedToggle from '../attention_requested_toggle.vue';
import AssigneeAvatarLink from './assignee_avatar_link.vue';
import UserNameWithStatus from './user_name_with_status.vue';
@ -10,7 +10,7 @@ const DEFAULT_RENDER_COUNT = 5;
export default {
components: {
AttentionRequiredToggle,
AttentionRequestedToggle,
AssigneeAvatarLink,
UserNameWithStatus,
},
@ -82,8 +82,8 @@ export default {
}
return u?.status?.availability || '';
},
toggleAttentionRequired(data) {
this.$emit('toggle-attention-required', data);
toggleAttentionRequested(data) {
this.$emit('toggle-attention-requested', data);
},
},
};
@ -113,11 +113,11 @@ export default {
}"
class="gl-display-inline-block"
>
<attention-required-toggle
<attention-requested-toggle
v-if="showVerticalList && user.can_update_merge_request"
:user="user"
type="assignee"
@toggle-attention-required="toggleAttentionRequired"
@toggle-attention-requested="toggleAttentionRequested"
/>
<assignee-avatar-link
:user="user"

View File

@ -5,9 +5,9 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
export default {
i18n: {
attentionRequiredReviewer: __('Request attention to review'),
attentionRequiredAssignee: __('Request attention'),
removeAttentionRequired: __('Remove attention request'),
attentionRequestedReviewer: __('Request attention to review'),
attentionRequestedAssignee: __('Request attention'),
removeAttentionRequested: __('Remove attention request'),
},
components: {
GlButton,
@ -32,13 +32,13 @@ export default {
},
computed: {
tooltipTitle() {
if (this.user.attention_required) {
return this.$options.i18n.removeAttentionRequired;
if (this.user.attention_requested) {
return this.$options.i18n.removeAttentionRequested;
}
return this.type === 'reviewer'
? this.$options.i18n.attentionRequiredReviewer
: this.$options.i18n.attentionRequiredAssignee;
? this.$options.i18n.attentionRequestedReviewer
: this.$options.i18n.attentionRequestedAssignee;
},
},
methods: {
@ -47,7 +47,7 @@ export default {
this.$root.$emit(BV_HIDE_TOOLTIP);
this.loading = true;
this.$emit('toggle-attention-required', {
this.$emit('toggle-attention-requested', {
user: this.user,
callback: this.toggleAttentionRequiredComplete,
});
@ -63,8 +63,8 @@ export default {
<span v-gl-tooltip.left.viewport="tooltipTitle">
<gl-button
:loading="loading"
:variant="user.attention_required ? 'warning' : 'default'"
:icon="user.attention_required ? 'star' : 'star-o'"
:variant="user.attention_requested ? 'warning' : 'default'"
:icon="user.attention_requested ? 'star' : 'star-o'"
:aria-label="tooltipTitle"
size="small"
category="tertiary"

View File

@ -49,8 +49,8 @@ export default {
requestReview(data) {
this.$emit('request-review', data);
},
toggleAttentionRequired(data) {
this.$emit('toggle-attention-required', data);
toggleAttentionRequested(data) {
this.$emit('toggle-attention-requested', data);
},
},
};
@ -73,7 +73,7 @@ export default {
:root-path="rootPath"
:issuable-type="issuableType"
@request-review="requestReview"
@toggle-attention-required="toggleAttentionRequired"
@toggle-attention-requested="toggleAttentionRequested"
/>
</div>
</div>

View File

@ -88,8 +88,8 @@ export default {
requestReview(data) {
this.mediator.requestReview(data);
},
toggleAttentionRequired(data) {
this.mediator.toggleAttentionRequired('reviewer', data);
toggleAttentionRequested(data) {
this.mediator.toggleAttentionRequested('reviewer', data);
},
},
};
@ -109,7 +109,7 @@ export default {
:editable="store.editable"
:issuable-type="issuableType"
@request-review="requestReview"
@toggle-attention-required="toggleAttentionRequired"
@toggle-attention-requested="toggleAttentionRequested"
/>
</div>
</template>

View File

@ -2,7 +2,7 @@
import { GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __, sprintf, s__ } from '~/locale';
import AttentionRequiredToggle from '../attention_required_toggle.vue';
import AttentionRequestedToggle from '../attention_requested_toggle.vue';
import ReviewerAvatarLink from './reviewer_avatar_link.vue';
const LOADING_STATE = 'loading';
@ -16,7 +16,7 @@ export default {
GlButton,
GlIcon,
ReviewerAvatarLink,
AttentionRequiredToggle,
AttentionRequestedToggle,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -80,8 +80,8 @@ export default {
this.loadingStates[userId] = null;
}
},
toggleAttentionRequired(data) {
this.$emit('toggle-attention-required', data);
toggleAttentionRequested(data) {
this.$emit('toggle-attention-requested', data);
},
},
LOADING_STATE,
@ -97,11 +97,11 @@ export default {
:class="{ 'gl-mb-3': index !== users.length - 1 }"
data-testid="reviewer"
>
<attention-required-toggle
<attention-requested-toggle
v-if="glFeatures.mrAttentionRequests && user.can_update_merge_request"
:user="user"
type="reviewer"
@toggle-attention-required="toggleAttentionRequired"
@toggle-attention-requested="toggleAttentionRequested"
/>
<reviewer-avatar-link :user="user" :root-path="rootPath" :issuable-type="issuableType">
<div class="gl-ml-3 gl-line-height-normal gl-display-grid">

View File

@ -1,5 +0,0 @@
mutation mergeRequestAttentionRequired($projectPath: ID!, $iid: String!, $userId: ID!) {
mergeRequestAttentionRequired(input: { projectPath: $projectPath, iid: $iid, userId: $userId }) {
errors
}
}

View File

@ -0,0 +1,7 @@
mutation mergeRequestToggleAttentionRequested($projectPath: ID!, $iid: String!, $userId: ID!) {
mergeRequestToggleAttentionRequested(
input: { projectPath: $projectPath, iid: $iid, userId: $userId }
) {
errors
}
}

View File

@ -5,7 +5,7 @@ import createGqClient, { fetchPolicies } from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils';
import reviewerRereviewMutation from '../queries/reviewer_rereview.mutation.graphql';
import sidebarDetailsMRQuery from '../queries/sidebarDetailsMR.query.graphql';
import attentionRequiredMutation from '../queries/attention_required.mutation.graphql';
import toggleAttentionRequestedMutation from '../queries/toggle_attention_requested.mutation.graphql';
const queries = {
merge_request: sidebarDetailsMRQuery,
@ -92,9 +92,9 @@ export default class SidebarService {
});
}
attentionRequired(userId) {
toggleAttentionRequested(userId) {
return gqClient.mutate({
mutation: attentionRequiredMutation,
mutation: toggleAttentionRequestedMutation,
variables: {
userId: convertToGraphQLId(TYPE_USER, `${userId}`),
projectPath: this.fullPath,

View File

@ -63,30 +63,27 @@ export default class SidebarMediator {
.catch(() => callback(userId, false));
}
async toggleAttentionRequired(type, { user, callback }) {
async toggleAttentionRequested(type, { user, callback }) {
try {
const isReviewer = type === 'reviewer';
const reviewerOrAssignee = isReviewer
? this.store.findReviewer(user)
: this.store.findAssignee(user);
if (reviewerOrAssignee.attention_required) {
await this.service.toggleAttentionRequested(user.id);
if (reviewerOrAssignee.attention_requested) {
toast(
sprintf(__('Removed attention request from @%{username}'), {
username: user.username,
}),
);
} else {
await this.service.attentionRequired(user.id);
toast(sprintf(__('Requested attention from @%{username}'), { username: user.username }));
}
if (isReviewer) {
this.store.updateReviewer(user.id, 'attention_required');
} else {
this.store.updateAssignee(user.id, 'attention_required');
}
this.store.updateReviewer(user.id, 'attention_requested');
this.store.updateAssignee(user.id, 'attention_requested');
callback();
} catch (error) {

View File

@ -24,10 +24,13 @@ export default {
},
},
data() {
const forceOpen = !this.collapsible || this.defaultExpanded;
return {
// Non-collapsible sections should always be expanded.
// For collapsible sections, fall back to defaultExpanded.
sectionExpanded: !this.collapsible || this.defaultExpanded,
sectionExpanded: forceOpen,
initialised: forceOpen,
animating: false,
};
},
computed: {
@ -53,7 +56,12 @@ export default {
toggleSectionExpanded() {
this.sectionExpanded = !this.sectionExpanded;
if (!this.initialised) {
this.initialised = true;
}
if (this.sectionExpanded) {
this.animating = true;
this.$refs.settingsContent.focus();
}
},
@ -68,7 +76,10 @@ export default {
</script>
<template>
<section class="settings" :class="{ 'no-animate': !slideAnimated, expanded: sectionExpanded }">
<section
class="settings"
:class="{ 'no-animate': !slideAnimated, expanded: sectionExpanded, animating }"
>
<div class="settings-header">
<h4>
<span
@ -103,12 +114,14 @@ export default {
</p>
</div>
<div
v-show="initialised"
:id="settingsContentId"
ref="settingsContent"
:aria-labelledby="settingsLabelId"
tabindex="-1"
role="region"
class="settings-content"
@animationend="animating = false"
>
<slot></slot>
</div>

View File

@ -2,20 +2,20 @@
module Mutations
module MergeRequests
class AttentionRequired < Base
graphql_name 'MergeRequestAttentionRequired'
class ToggleAttentionRequested < Base
graphql_name 'MergeRequestToggleAttentionRequested'
argument :user_id, ::Types::GlobalIDType[::User],
loads: Types::UserType,
required: true,
description: <<~DESC
User ID for the user that has their attention requested.
User ID for the user to toggle attention requested.
DESC
def resolve(project_path:, iid:, user:)
merge_request = authorized_find!(project_path: project_path, iid: iid)
result = ::MergeRequests::AttentionRequiredService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: user).execute
result = ::MergeRequests::ToggleAttentionRequestedService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: user).execute
{
merge_request: merge_request,

View File

@ -68,7 +68,7 @@ module Types
mount_mutation Mutations::MergeRequests::SetDraft, calls_gitaly: true
mount_mutation Mutations::MergeRequests::SetAssignees
mount_mutation Mutations::MergeRequests::ReviewerRereview
mount_mutation Mutations::MergeRequests::AttentionRequired, feature_flag: :mr_attention_requests
mount_mutation Mutations::MergeRequests::ToggleAttentionRequested, feature_flag: :mr_attention_requests
mount_mutation Mutations::Metrics::Dashboard::Annotations::Create
mount_mutation Mutations::Metrics::Dashboard::Annotations::Delete
mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true

View File

@ -24,7 +24,7 @@ module TodosHelper
when Todo::UNMERGEABLE then 'Could not merge'
when Todo::DIRECTLY_ADDRESSED then "directly addressed #{todo_action_subject(todo)} on"
when Todo::MERGE_TRAIN_REMOVED then "Removed from Merge Train:"
when Todo::ATTENTION_REQUIRED then 'requested your attention on'
when Todo::ATTENTION_REQUESTED then 'requested your attention on'
end
end

View File

@ -7,7 +7,7 @@ module MergeRequestReviewerState
enum state: {
unreviewed: 0,
reviewed: 1,
attention_required: 2
attention_requested: 2
}
validates :state,
@ -18,7 +18,7 @@ module MergeRequestReviewerState
def set_state
if Feature.enabled?(:mr_attention_requests, self.merge_request&.project, default_enabled: :yaml)
self.state = :attention_required
self.state = :attention_requested
end
end
end

View File

@ -1945,7 +1945,7 @@ class MergeRequest < ApplicationRecord
end
end
def attention_required_enabled?
def attention_requested_enabled?
Feature.enabled?(:mr_attention_requests, project, default_enabled: :yaml)
end

View File

@ -18,7 +18,7 @@ class Todo < ApplicationRecord
DIRECTLY_ADDRESSED = 7
MERGE_TRAIN_REMOVED = 8 # This is an EE-only feature
REVIEW_REQUESTED = 9
ATTENTION_REQUIRED = 10
ATTENTION_REQUESTED = 10
ACTION_NAMES = {
ASSIGNED => :assigned,
@ -30,7 +30,7 @@ class Todo < ApplicationRecord
UNMERGEABLE => :unmergeable,
DIRECTLY_ADDRESSED => :directly_addressed,
MERGE_TRAIN_REMOVED => :merge_train_removed,
ATTENTION_REQUIRED => :attention_required
ATTENTION_REQUESTED => :attention_requested
}.freeze
belongs_to :author, class_name: "User"
@ -191,8 +191,8 @@ class Todo < ApplicationRecord
action == REVIEW_REQUESTED
end
def attention_required?
action == ATTENTION_REQUIRED
def attention_requested?
action == ATTENTION_REQUESTED
end
def merge_train_removed?

View File

@ -20,8 +20,8 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic
find_reviewer_or_assignee(user, options)&.reviewed?
end
expose :attention_required, if: satisfies(:present?, :allows_reviewers?, :attention_required_enabled?) do |user, options|
find_reviewer_or_assignee(user, options)&.attention_required?
expose :attention_requested, if: satisfies(:present?, :allows_reviewers?, :attention_requested_enabled?) do |user, options|
find_reviewer_or_assignee(user, options)&.attention_requested?
end
expose :approved, if: satisfies(:present?) do |user, options|

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module MergeRequests
class AttentionRequiredService < MergeRequests::BaseService
class ToggleAttentionRequestedService < MergeRequests::BaseService
attr_accessor :merge_request, :user
def initialize(project:, current_user:, merge_request:, user:)
@ -15,10 +15,12 @@ module MergeRequests
return error("Invalid permissions") unless can?(current_user, :update_merge_request, merge_request)
if reviewer || assignee
reviewer&.update(state: :attention_required)
assignee&.update(state: :attention_required)
update_state(reviewer)
update_state(assignee)
notity_user
if reviewer&.attention_requested? || assignee&.attention_requested?
notity_user
end
success
else
@ -29,7 +31,7 @@ module MergeRequests
private
def notity_user
todo_service.create_attention_required_todo(merge_request, current_user, user)
todo_service.create_attention_requested_todo(merge_request, current_user, user)
end
def assignee
@ -39,5 +41,9 @@ module MergeRequests
def reviewer
merge_request.find_reviewer(user)
end
def update_state(reviewer_or_assignee)
reviewer_or_assignee&.update(state: reviewer_or_assignee&.attention_requested? ? :reviewed : :attention_requested)
end
end
end

View File

@ -217,8 +217,8 @@ class TodoService
create_todos(reviewers, attributes)
end
def create_attention_required_todo(target, author, users)
attributes = attributes_for_todo(target.project, target, author, Todo::ATTENTION_REQUIRED)
def create_attention_requested_todo(target, author, users)
attributes = attributes_for_todo(target.project, target, author, Todo::ATTENTION_REQUESTED)
create_todos(users, attributes)
end

View File

@ -37,7 +37,27 @@ If an existing GitLab user wants to enable LDAP sign-in for themselves, they sho
1. Check that their GitLab email address matches their LDAP email address.
1. Sign in to GitLab by using their LDAP credentials.
## Security risks
## Security
GitLab has multiple mechanisms to verify a user is still active in LDAP. If the user is no longer active in
LDAP, they are placed in an `ldap_blocked` status and are signed out. They are unable to sign in using any authentication provider until they are
reactivated in LDAP.
Users are considered inactive in LDAP when they:
- Are removed from the directory completely.
- Reside outside the configured `base` DN or `user_filter` search.
- Are marked as disabled or deactivated in Active Directory through the user account control attribute. This means attribute
`userAccountControl:1.2.840.113556.1.4.803` has bit 2 set.
Status is checked for all LDAP users:
- When signing in using any authentication provider.
- Once per hour for active web sessions or Git requests using tokens or SSH keys.
- When performing Git over HTTP requests using LDAP username and password.
- Once per day during [User Sync](ldap_synchronization.md#user-sync).
### Security risks
You should only use LDAP integration if your LDAP users cannot:

View File

@ -649,7 +649,7 @@ To override the global maximum pages size for a specific group:
## Running GitLab Pages on a separate server
You can run the GitLab Pages daemon on a separate server to decrease the load on
your main application server.
your main application server. This configuration does not support mutual TLS (mTLS). See the [corresponding feature proposal](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/548) for more information.
To configure GitLab Pages on a separate server:

View File

@ -3313,29 +3313,6 @@ Input type: `MergeRequestAcceptInput`
| <a id="mutationmergerequestaccepterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestacceptmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestAttentionRequired`
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
Input type: `MergeRequestAttentionRequiredInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequestattentionrequiredclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequestattentionrequirediid"></a>`iid` | [`String!`](#string) | IID of the merge request to mutate. |
| <a id="mutationmergerequestattentionrequiredprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the merge request to mutate is in. |
| <a id="mutationmergerequestattentionrequireduserid"></a>`userId` | [`UserID!`](#userid) | User ID for the user that has their attention requested. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequestattentionrequiredclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequestattentionrequirederrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestattentionrequiredmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestCreate`
Input type: `MergeRequestCreateInput`
@ -3509,6 +3486,29 @@ Input type: `MergeRequestSetSubscriptionInput`
| <a id="mutationmergerequestsetsubscriptionerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestsetsubscriptionmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestToggleAttentionRequested`
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
Input type: `MergeRequestToggleAttentionRequestedInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequesttoggleattentionrequestedclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequesttoggleattentionrequestediid"></a>`iid` | [`String!`](#string) | IID of the merge request to mutate. |
| <a id="mutationmergerequesttoggleattentionrequestedprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the merge request to mutate is in. |
| <a id="mutationmergerequesttoggleattentionrequesteduserid"></a>`userId` | [`UserID!`](#userid) | User ID for the user to toggle attention requested. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequesttoggleattentionrequestedclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequesttoggleattentionrequestederrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequesttoggleattentionrequestedmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestUpdate`
Update attributes of a merge request.
@ -16474,7 +16474,7 @@ State of a review of a GitLab merge request.
| Value | Description |
| ----- | ----------- |
| <a id="mergerequestreviewstateattention_required"></a>`ATTENTION_REQUIRED` | The merge request is attention_required. |
| <a id="mergerequestreviewstateattention_requested"></a>`ATTENTION_REQUESTED` | The merge request is attention_requested. |
| <a id="mergerequestreviewstatereviewed"></a>`REVIEWED` | The merge request is reviewed. |
| <a id="mergerequestreviewstateunreviewed"></a>`UNREVIEWED` | The merge request is unreviewed. |

View File

@ -31,10 +31,10 @@ module Gitlab
namespace_options = options.merge(null: true, default: nil)
add_column(:namespace_settings, setting_name, type, namespace_options)
add_column(:namespace_settings, setting_name, type, **namespace_options)
add_column(:namespace_settings, lock_column_name, :boolean, default: false, null: false)
add_column(:application_settings, setting_name, type, options)
add_column(:application_settings, setting_name, type, **options)
add_column(:application_settings, lock_column_name, :boolean, default: false, null: false)
end

View File

@ -30609,7 +30609,7 @@ msgstr ""
msgid "SecurityConfiguration|Available with Ultimate"
msgstr ""
msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the Merge Request."
msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the merge request."
msgstr ""
msgid "SecurityConfiguration|Compliance"
@ -30636,7 +30636,7 @@ msgstr ""
msgid "SecurityConfiguration|Could not retrieve configuration data. Please refresh the page, or try again later."
msgstr ""
msgid "SecurityConfiguration|Create Merge Request"
msgid "SecurityConfiguration|Create merge request"
msgstr ""
msgid "SecurityConfiguration|Customize common SAST settings to suit your requirements. Configuration changes made here override those provided by GitLab and are excluded from updates. For details of more advanced configuration options, see the %{linkStart}GitLab SAST documentation%{linkEnd}."
@ -33042,7 +33042,7 @@ msgstr ""
msgid "StatusCheck|Apply this status check to all branches or a specific protected branch."
msgstr ""
msgid "StatusCheck|Check for a status response in Merge Requests. Failures do not block merges. %{link_start}Learn more%{link_end}."
msgid "StatusCheck|Check for a status response in merge requests. Failures do not block merges. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "StatusCheck|Examples: QA, Security."

View File

@ -1,11 +1,11 @@
import { GlButton } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import AttentionRequiredToggle from '~/sidebar/components/attention_required_toggle.vue';
import AttentionRequestedToggle from '~/sidebar/components/attention_requested_toggle.vue';
let wrapper;
function factory(propsData = {}) {
wrapper = mount(AttentionRequiredToggle, { propsData });
wrapper = mount(AttentionRequestedToggle, { propsData });
}
const findToggle = () => wrapper.findComponent(GlButton);
@ -16,52 +16,52 @@ describe('Attention require toggle', () => {
});
it('renders button', () => {
factory({ type: 'reviewer', user: { attention_required: false } });
factory({ type: 'reviewer', user: { attention_requested: false } });
expect(findToggle().exists()).toBe(true);
});
it.each`
attentionRequired | icon
${true} | ${'star'}
${false} | ${'star-o'}
attentionRequested | icon
${true} | ${'star'}
${false} | ${'star-o'}
`(
'renders $icon icon when attention_required is $attentionRequired',
({ attentionRequired, icon }) => {
factory({ type: 'reviewer', user: { attention_required: attentionRequired } });
'renders $icon icon when attention_requested is $attentionRequested',
({ attentionRequested, icon }) => {
factory({ type: 'reviewer', user: { attention_requested: attentionRequested } });
expect(findToggle().props('icon')).toBe(icon);
},
);
it.each`
attentionRequired | variant
${true} | ${'warning'}
${false} | ${'default'}
attentionRequested | variant
${true} | ${'warning'}
${false} | ${'default'}
`(
'renders button with variant $variant when attention_required is $attentionRequired',
({ attentionRequired, variant }) => {
factory({ type: 'reviewer', user: { attention_required: attentionRequired } });
'renders button with variant $variant when attention_requested is $attentionRequested',
({ attentionRequested, variant }) => {
factory({ type: 'reviewer', user: { attention_requested: attentionRequested } });
expect(findToggle().props('variant')).toBe(variant);
},
);
it('emits toggle-attention-required on click', async () => {
factory({ type: 'reviewer', user: { attention_required: true } });
it('emits toggle-attention-requested on click', async () => {
factory({ type: 'reviewer', user: { attention_requested: true } });
await findToggle().trigger('click');
expect(wrapper.emitted('toggle-attention-required')[0]).toEqual([
expect(wrapper.emitted('toggle-attention-requested')[0]).toEqual([
{
user: { attention_required: true },
user: { attention_requested: true },
callback: expect.anything(),
},
]);
});
it('sets loading on click', async () => {
factory({ type: 'reviewer', user: { attention_required: true } });
factory({ type: 'reviewer', user: { attention_requested: true } });
await findToggle().trigger('click');
@ -69,14 +69,14 @@ describe('Attention require toggle', () => {
});
it.each`
type | attentionRequired | tooltip
${'reviewer'} | ${true} | ${AttentionRequiredToggle.i18n.removeAttentionRequired}
${'reviewer'} | ${false} | ${AttentionRequiredToggle.i18n.attentionRequiredReviewer}
${'assignee'} | ${false} | ${AttentionRequiredToggle.i18n.attentionRequiredAssignee}
type | attentionRequested | tooltip
${'reviewer'} | ${true} | ${AttentionRequestedToggle.i18n.removeAttentionRequested}
${'reviewer'} | ${false} | ${AttentionRequestedToggle.i18n.attentionRequestedReviewer}
${'assignee'} | ${false} | ${AttentionRequestedToggle.i18n.attentionRequestedAssignee}
`(
'sets tooltip as $tooltip when attention_required is $attentionRequired and type is $type',
({ type, attentionRequired, tooltip }) => {
factory({ type, user: { attention_required: attentionRequired } });
'sets tooltip as $tooltip when attention_requested is $attentionRequested and type is $type',
({ type, attentionRequested, tooltip }) => {
factory({ type, user: { attention_requested: attentionRequested } });
expect(findToggle().attributes('aria-label')).toBe(tooltip);
},

View File

@ -1,6 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants';
import AttentionRequiredToggle from '~/sidebar/components/attention_required_toggle.vue';
import AttentionRequestedToggle from '~/sidebar/components/attention_requested_toggle.vue';
import ReviewerAvatarLink from '~/sidebar/components/reviewers/reviewer_avatar_link.vue';
import UncollapsedReviewerList from '~/sidebar/components/reviewers/uncollapsed_reviewer_list.vue';
import userDataMock from '../../user_data_mock';
@ -121,11 +121,11 @@ describe('UncollapsedReviewerList component', () => {
expect(wrapper.findAll('[data-testid="re-request-button"]').length).toBe(0);
});
it('emits toggle-attention-required', () => {
it('emits toggle-attention-requested', () => {
createComponent({ users: [userDataMock()] }, { mrAttentionRequests: true });
wrapper.find(AttentionRequiredToggle).vm.$emit('toggle-attention-required', 'data');
wrapper.find(AttentionRequestedToggle).vm.$emit('toggle-attention-requested', 'data');
expect(wrapper.emitted('toggle-attention-required')[0]).toEqual(['data']);
expect(wrapper.emitted('toggle-attention-requested')[0]).toEqual(['data']);
});
});

View File

@ -119,19 +119,19 @@ describe('Sidebar mediator', () => {
});
});
describe('toggleAttentionRequired', () => {
describe('toggleAttentionRequested', () => {
let attentionRequiredService;
beforeEach(() => {
attentionRequiredService = jest
.spyOn(mediator.service, 'attentionRequired')
.spyOn(mediator.service, 'toggleAttentionRequested')
.mockResolvedValue();
});
it('calls attentionRequired service method', async () => {
mediator.store.reviewers = [{ id: 1, attention_required: false, username: 'root' }];
mediator.store.reviewers = [{ id: 1, attention_requested: false, username: 'root' }];
await mediator.toggleAttentionRequired('reviewer', {
await mediator.toggleAttentionRequested('reviewer', {
user: { id: 1, username: 'root' },
callback: jest.fn(),
});
@ -145,23 +145,23 @@ describe('Sidebar mediator', () => {
`('finds $type', ({ type, method }) => {
const methodSpy = jest.spyOn(mediator.store, method);
mediator.toggleAttentionRequired(type, { user: { id: 1 }, callback: jest.fn() });
mediator.toggleAttentionRequested(type, { user: { id: 1 }, callback: jest.fn() });
expect(methodSpy).toHaveBeenCalledWith({ id: 1 });
});
it.each`
attentionRequired | toastMessage
${true} | ${'Removed attention request from @root'}
${false} | ${'Requested attention from @root'}
attentionRequested | toastMessage
${true} | ${'Removed attention request from @root'}
${false} | ${'Requested attention from @root'}
`(
'it creates toast $toastMessage when attention_required is $attentionRequired',
async ({ attentionRequired, toastMessage }) => {
'it creates toast $toastMessage when attention_requested is $attentionRequested',
async ({ attentionRequested, toastMessage }) => {
mediator.store.reviewers = [
{ id: 1, attention_required: attentionRequired, username: 'root' },
{ id: 1, attention_requested: attentionRequested, username: 'root' },
];
await mediator.toggleAttentionRequired('reviewer', {
await mediator.toggleAttentionRequested('reviewer', {
user: { id: 1, username: 'root' },
callback: jest.fn(),
});

View File

@ -50,6 +50,7 @@ exports[`Settings Block renders the correct markup 1`] = `
class="settings-content"
id="settings_content_3"
role="region"
style="display: none;"
tabindex="-1"
>
<div

View File

@ -1,12 +1,12 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
describe('Settings Block', () => {
let wrapper;
const mountComponent = (propsData) => {
wrapper = shallowMount(SettingsBlock, {
wrapper = shallowMountExtended(SettingsBlock, {
propsData,
slots: {
title: '<div data-testid="title-slot"></div>',
@ -20,11 +20,13 @@ describe('Settings Block', () => {
wrapper.destroy();
});
const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]');
const findTitleSlot = () => wrapper.find('[data-testid="title-slot"]');
const findDescriptionSlot = () => wrapper.find('[data-testid="description-slot"]');
const findDefaultSlot = () => wrapper.findByTestId('default-slot');
const findTitleSlot = () => wrapper.findByTestId('title-slot');
const findDescriptionSlot = () => wrapper.findByTestId('description-slot');
const findExpandButton = () => wrapper.findComponent(GlButton);
const findSectionTitleButton = () => wrapper.find('[data-testid="section-title-button"]');
const findSectionTitleButton = () => wrapper.findByTestId('section-title-button');
// we are using a non js class for this finder because this class determine the component structure
const findSettingsContent = () => wrapper.find('.settings-content');
const expectExpandedState = ({ expanded = true } = {}) => {
const settingsExpandButton = findExpandButton();
@ -62,6 +64,26 @@ describe('Settings Block', () => {
expect(findDescriptionSlot().exists()).toBe(true);
});
it('content is hidden before first expansion', async () => {
// this is a regression test for the bug described here: https://gitlab.com/gitlab-org/gitlab/-/issues/331774
mountComponent();
// content is hidden
expect(findDefaultSlot().isVisible()).toBe(false);
// expand
await findSectionTitleButton().trigger('click');
// content is visible
expect(findDefaultSlot().isVisible()).toBe(true);
// collapse
await findSectionTitleButton().trigger('click');
// content is still visible (and we have a closing animation)
expect(findDefaultSlot().isVisible()).toBe(true);
});
describe('slide animation behaviour', () => {
it('is animated by default', () => {
mountComponent();
@ -81,6 +103,20 @@ describe('Settings Block', () => {
expect(wrapper.classes('no-animate')).toBe(noAnimatedClass);
},
);
it('sets the animating class only during the animation', async () => {
mountComponent();
expect(wrapper.classes('animating')).toBe(false);
await findSectionTitleButton().trigger('click');
expect(wrapper.classes('animating')).toBe(true);
await findSettingsContent().trigger('animationend');
expect(wrapper.classes('animating')).toBe(false);
});
});
describe('expanded behaviour', () => {

View File

@ -13,9 +13,9 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewState'] do
description: 'The merge request is unreviewed.',
value: 'unreviewed'
),
'ATTENTION_REQUIRED' => have_attributes(
description: 'The merge request is attention_required.',
value: 'attention_required'
'ATTENTION_REQUESTED' => have_attributes(
description: 'The merge request is attention_requested.',
value: 'attention_requested'
)
)
end

View File

@ -78,7 +78,7 @@ RSpec.describe GitlabSchema.types['UserMergeRequestInteraction'] do
merge_request.reviewers << user
end
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUIRED'].value) }
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
it 'implies not reviewed' do
expect(resolve(:reviewed)).to be false

View File

@ -61,7 +61,7 @@ RSpec.describe ::Users::MergeRequestInteraction do
merge_request.reviewers << user
end
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUIRED'].value) }
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
it 'implies not reviewed' do
expect(interaction).not_to be_reviewed

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Setting attention required for reviewer' do
RSpec.describe 'Toggle attention requested for reviewer' do
include GraphqlHelpers
let(:current_user) { create(:user) }
@ -16,7 +16,7 @@ RSpec.describe 'Setting attention required for reviewer' do
project_path: project.full_path,
iid: merge_request.iid.to_s
}
graphql_mutation(:merge_request_attention_required, variables.merge(input),
graphql_mutation(:merge_request_toggle_attention_requested, variables.merge(input),
<<-QL.strip_heredoc
clientMutationId
errors
@ -25,7 +25,7 @@ RSpec.describe 'Setting attention required for reviewer' do
end
def mutation_response
graphql_mutation_response(:merge_request_attention_required)
graphql_mutation_response(:merge_request_toggle_attention_requested)
end
def mutation_errors

View File

@ -347,7 +347,7 @@ RSpec.describe 'getting merge request information nested in a project' do
expect(interaction_data).to contain_exactly a_hash_including(
'canMerge' => false,
'canUpdate' => can_update,
'reviewState' => attention_required,
'reviewState' => attention_requested,
'reviewed' => false,
'approved' => false
)
@ -380,8 +380,8 @@ RSpec.describe 'getting merge request information nested in a project' do
describe 'scalability' do
let_it_be(:other_users) { create_list(:user, 3) }
let(:attention_required) do
{ 'reviewState' => 'ATTENTION_REQUIRED' }
let(:attention_requested) do
{ 'reviewState' => 'ATTENTION_REQUESTED' }
end
let(:reviewed) do
@ -413,9 +413,9 @@ RSpec.describe 'getting merge request information nested in a project' do
expect { post_graphql(query) }.not_to exceed_query_limit(baseline)
expect(interaction_data).to contain_exactly(
include(attention_required),
include(attention_required),
include(attention_required),
include(attention_requested),
include(attention_requested),
include(attention_requested),
include(reviewed)
)
end
@ -444,7 +444,7 @@ RSpec.describe 'getting merge request information nested in a project' do
it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :reviewers }
let(:attention_required) { 'ATTENTION_REQUIRED' }
let(:attention_requested) { 'ATTENTION_REQUESTED' }
let(:can_update) { false }
def assign_user(user)
@ -454,7 +454,7 @@ RSpec.describe 'getting merge request information nested in a project' do
it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :assignees }
let(:attention_required) { nil }
let(:attention_requested) { nil }
let(:can_update) { true } # assignees can update MRs
def assign_user(user)

View File

@ -19,7 +19,7 @@ RSpec.describe MergeRequestUserEntity do
is_expected.to include(
:id, :name, :username, :state, :avatar_url, :web_url,
:can_merge, :can_update_merge_request, :reviewed, :approved,
:attention_required
:attention_requested
)
end
@ -57,8 +57,8 @@ RSpec.describe MergeRequestUserEntity do
end
end
context 'attention_required' do
it { is_expected.to include(attention_required: true ) }
context 'attention_requested' do
it { is_expected.to include(attention_requested: true ) }
end
describe 'performance' do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe MergeRequests::AttentionRequiredService do
RSpec.describe MergeRequests::ToggleAttentionRequestedService do
let(:current_user) { create(:user) }
let(:user) { create(:user) }
let(:assignee_user) { create(:user) }
@ -39,6 +39,10 @@ RSpec.describe MergeRequests::AttentionRequiredService do
end
context 'reviewer exists' do
before do
reviewer.update!(state: :reviewed)
end
it 'returns success' do
expect(result[:status]).to eq :success
end
@ -47,11 +51,11 @@ RSpec.describe MergeRequests::AttentionRequiredService do
service.execute
reviewer.reload
expect(reviewer.state).to eq 'attention_required'
expect(reviewer.state).to eq 'attention_requested'
end
it 'creates a new todo for the reviewer' do
expect(todo_service).to receive(:create_attention_required_todo).with(merge_request, current_user, user)
expect(todo_service).to receive(:create_attention_requested_todo).with(merge_request, current_user, user)
service.execute
end
@ -60,6 +64,10 @@ RSpec.describe MergeRequests::AttentionRequiredService do
context 'assignee exists' do
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: assignee_user) }
before do
assignee.update!(state: :reviewed)
end
it 'returns success' do
expect(result[:status]).to eq :success
end
@ -68,11 +76,11 @@ RSpec.describe MergeRequests::AttentionRequiredService do
service.execute
assignee.reload
expect(assignee.state).to eq 'attention_required'
expect(assignee.state).to eq 'attention_requested'
end
it 'creates a new todo for the reviewer' do
expect(todo_service).to receive(:create_attention_required_todo).with(merge_request, current_user, assignee_user)
expect(todo_service).to receive(:create_attention_requested_todo).with(merge_request, current_user, assignee_user)
service.execute
end
@ -83,13 +91,37 @@ RSpec.describe MergeRequests::AttentionRequiredService do
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: user) }
let(:assignee) { merge_request.find_assignee(user) }
before do
reviewer.update!(state: :reviewed)
assignee.update!(state: :reviewed)
end
it 'updates reviewers and assignees state' do
service.execute
reviewer.reload
assignee.reload
expect(reviewer.state).to eq 'attention_required'
expect(assignee.state).to eq 'attention_required'
expect(reviewer.state).to eq 'attention_requested'
expect(assignee.state).to eq 'attention_requested'
end
end
context 'state is attention_requested' do
before do
reviewer.update!(state: :attention_requested)
end
it 'toggles state to reviewed' do
service.execute
reviewer.reload
expect(reviewer.state).to eq "reviewed"
end
it 'does not create a new todo for the reviewer' do
expect(todo_service).not_to receive(:create_attention_requested_todo).with(merge_request, current_user, assignee_user)
service.execute
end
end
end

View File

@ -1218,14 +1218,14 @@ RSpec.describe TodoService do
end
end
describe '#create_attention_required_todo' do
describe '#create_attention_requested_todo' do
let(:target) { create(:merge_request, author: author, source_project: project) }
let(:user) { create(:user) }
it 'creates a todo for user' do
service.create_attention_required_todo(target, author, user)
service.create_attention_requested_todo(target, author, user)
should_create_todo(user: user, target: target, action: Todo::ATTENTION_REQUIRED)
should_create_todo(user: user, target: target, action: Todo::ATTENTION_REQUESTED)
end
end

View File

@ -10,6 +10,6 @@ RSpec.shared_examples 'having reviewer state' do
end
describe 'mr_attention_requests feature flag is enabled' do
it { is_expected.to have_attributes(state: 'attention_required') }
it { is_expected.to have_attributes(state: 'attention_requested') }
end
end

View File

@ -7,8 +7,10 @@ require 'danger/plugins/helper'
require 'gitlab/dangerfiles/spec_helper'
require_relative '../../../danger/plugins/project_helper'
require_relative '../../../spec/support/helpers/stub_env'
RSpec.describe Tooling::Danger::ProjectHelper do
include StubENV
include_context "with dangerfile"
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
@ -341,6 +343,70 @@ RSpec.describe Tooling::Danger::ProjectHelper do
end
end
describe '#ee?' do
subject { project_helper.__send__(:ee?) }
let(:ee_dir) { File.expand_path('../../../ee', __dir__) }
context 'when ENV["CI_PROJECT_NAME"] is set' do
before do
stub_env('CI_PROJECT_NAME', ci_project_name)
end
context 'when ENV["CI_PROJECT_NAME"] is gitlab' do
let(:ci_project_name) { 'gitlab' }
it 'returns true' do
is_expected.to eq(true)
end
end
context 'when ENV["CI_PROJECT_NAME"] is gitlab-ee' do
let(:ci_project_name) { 'gitlab-ee' }
it 'returns true' do
is_expected.to eq(true)
end
end
context 'when ENV["CI_PROJECT_NAME"] is gitlab-foss' do
let(:ci_project_name) { 'gitlab-foss' }
it 'resolves to Dir.exist?' do
expected = Dir.exist?(ee_dir)
expect(Dir).to receive(:exist?).with(ee_dir).and_call_original
is_expected.to eq(expected)
end
end
end
context 'when ENV["CI_PROJECT_NAME"] is absent' do
before do
stub_env('CI_PROJECT_NAME', nil)
expect(Dir).to receive(:exist?).with(ee_dir).and_return(has_ee_dir)
end
context 'when ee/ directory exists' do
let(:has_ee_dir) { true }
it 'returns true' do
is_expected.to eq(true)
end
end
context 'when ee/ directory does not exist' do
let(:has_ee_dir) { false }
it 'returns false' do
is_expected.to eq(false)
end
end
end
end
describe '#file_lines' do
let(:filename) { 'spec/foo_spec.rb' }
let(:file_spy) { spy }

View File

@ -224,7 +224,7 @@ module Tooling
def ee?
# Support former project name for `dev` and support local Danger run
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?(File.expand_path('../../../ee', __dir__))
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?(File.expand_path('../../ee', __dir__))
end
end
end