Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f9e0126cad
commit
049fd8333a
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
mutation mergeRequestAttentionRequired($projectPath: ID!, $iid: String!, $userId: ID!) {
|
||||
mergeRequestAttentionRequired(input: { projectPath: $projectPath, iid: $iid, userId: $userId }) {
|
||||
errors
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
mutation mergeRequestToggleAttentionRequested($projectPath: ID!, $iid: String!, $userId: ID!) {
|
||||
mergeRequestToggleAttentionRequested(
|
||||
input: { projectPath: $projectPath, iid: $iid, userId: $userId }
|
||||
) {
|
||||
errors
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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. |
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue