Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e6f170581e
commit
ab9c1dbb2d
3
Gemfile
3
Gemfile
|
@ -352,8 +352,9 @@ end
|
|||
group :development, :test do
|
||||
gem 'deprecation_toolkit', '~> 1.5.1', require: false
|
||||
gem 'bullet', '~> 6.1.0'
|
||||
gem 'pry-byebug', '~> 3.9.0', platform: :mri
|
||||
gem 'gitlab-pry-byebug', platform: :mri, require: ['pry-byebug', 'pry-byebug/pry_remote_ext']
|
||||
gem 'pry-rails', '~> 0.3.9'
|
||||
gem 'pry-remote'
|
||||
|
||||
gem 'awesome_print', require: false
|
||||
|
||||
|
|
13
Gemfile.lock
13
Gemfile.lock
|
@ -445,6 +445,9 @@ GEM
|
|||
gitlab-markup (1.7.1)
|
||||
gitlab-net-dns (0.9.1)
|
||||
gitlab-pg_query (1.3.1)
|
||||
gitlab-pry-byebug (3.9.0)
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.13.0)
|
||||
gitlab-sidekiq-fetcher (0.5.2)
|
||||
sidekiq (~> 5)
|
||||
gitlab-styles (6.0.0)
|
||||
|
@ -864,11 +867,11 @@ GEM
|
|||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-byebug (3.9.0)
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.13.0)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
pry-remote (0.1.8)
|
||||
pry (~> 0.9)
|
||||
slop (~> 3.0)
|
||||
public_suffix (4.0.6)
|
||||
puma (5.1.1)
|
||||
nio4r (~> 2.0)
|
||||
|
@ -1131,6 +1134,7 @@ GEM
|
|||
simplecov-html (0.12.2)
|
||||
sixarm_ruby_unaccent (1.2.0)
|
||||
slack-messenger (2.3.4)
|
||||
slop (3.6.0)
|
||||
snowplow-tracker (0.6.1)
|
||||
contracts (~> 0.7, <= 0.11)
|
||||
spring (2.1.1)
|
||||
|
@ -1364,6 +1368,7 @@ DEPENDENCIES
|
|||
gitlab-mail_room (~> 0.0.8)
|
||||
gitlab-markup (~> 1.7.1)
|
||||
gitlab-net-dns (~> 0.9.1)
|
||||
gitlab-pry-byebug
|
||||
gitlab-sidekiq-fetcher (= 0.5.2)
|
||||
gitlab-styles (~> 6.0.0)
|
||||
gitlab_chronic_duration (~> 0.10.6.2)
|
||||
|
@ -1455,8 +1460,8 @@ DEPENDENCIES
|
|||
png_quantizator (~> 0.2.1)
|
||||
premailer-rails (~> 1.10.3)
|
||||
prometheus-client-mmap (~> 0.12.0)
|
||||
pry-byebug (~> 3.9.0)
|
||||
pry-rails (~> 0.3.9)
|
||||
pry-remote
|
||||
puma (~> 5.1.1)
|
||||
puma_worker_killer (~> 0.3.1)
|
||||
rack (~> 2.2.3)
|
||||
|
|
|
@ -5,12 +5,10 @@ import MembersTable from '~/members/components/table/members_table.vue';
|
|||
import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue';
|
||||
import { scrollToElement } from '~/lib/utils/common_utils';
|
||||
import { HIDE_ERROR } from '~/members/store/mutation_types';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
export default {
|
||||
name: 'GroupMembersApp',
|
||||
components: { MembersTable, FilterSortContainer, GlAlert },
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
computed: {
|
||||
...mapState(['showError', 'errorMessage']),
|
||||
},
|
||||
|
@ -36,7 +34,7 @@ export default {
|
|||
<gl-alert v-if="showError" ref="errorAlert" variant="danger" @dismiss="hideError">{{
|
||||
errorMessage
|
||||
}}</gl-alert>
|
||||
<filter-sort-container v-if="glFeatures.groupMembersFilteredSearch" />
|
||||
<filter-sort-container />
|
||||
<members-table />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
export default {
|
||||
name: 'JiraConnectApp',
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
computed: {
|
||||
state() {
|
||||
return this.$root.$data.state || {};
|
||||
},
|
||||
error() {
|
||||
return this.state.error;
|
||||
},
|
||||
...mapState(['errorMessage']),
|
||||
showNewUi() {
|
||||
return this.glFeatures.newJiraConnectUi;
|
||||
},
|
||||
|
@ -21,7 +17,7 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div v-if="showNewUi">
|
||||
<h3>{{ s__('Integrations|Linked namespaces') }}</h3>
|
||||
<h3 data-testid="new-jira-connect-ui-heading">{{ s__('Integrations|Linked namespaces') }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import $ from 'jquery';
|
||||
import setConfigs from '@gitlab/ui/dist/config';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import GlFeatureFlagsPlugin from '~/vue_shared/gl_feature_flags_plugin';
|
||||
|
||||
import App from './components/app.vue';
|
||||
import JiraConnectApp from './components/app.vue';
|
||||
import { addSubscription, removeSubscription } from '~/jira_connect/api';
|
||||
import createStore from './store';
|
||||
import { SET_ERROR_MESSAGE } from './store/mutation_types';
|
||||
|
||||
const store = {
|
||||
state: {
|
||||
error: '',
|
||||
},
|
||||
setErrorMessage(errorMessage) {
|
||||
this.state.error = errorMessage;
|
||||
},
|
||||
};
|
||||
Vue.use(Vuex);
|
||||
|
||||
const store = createStore();
|
||||
|
||||
/**
|
||||
* Initialize necessary form handlers for the Jira Connect app
|
||||
* Initialize form handlers for the Jira Connect app
|
||||
*/
|
||||
const initJiraFormHandlers = () => {
|
||||
const reqComplete = () => {
|
||||
|
@ -27,7 +25,7 @@ const initJiraFormHandlers = () => {
|
|||
const reqFailed = (res, fallbackErrorMessage) => {
|
||||
const { responseJSON: { error = fallbackErrorMessage } = {} } = res || {};
|
||||
|
||||
store.setErrorMessage(error);
|
||||
store.commit(SET_ERROR_MESSAGE, error);
|
||||
// eslint-disable-next-line no-alert
|
||||
alert(error);
|
||||
};
|
||||
|
@ -79,11 +77,9 @@ function initJiraConnect() {
|
|||
|
||||
return new Vue({
|
||||
el,
|
||||
data: {
|
||||
state: store.state,
|
||||
},
|
||||
store,
|
||||
render(createElement) {
|
||||
return createElement(App, {});
|
||||
return createElement(JiraConnectApp, {});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import Vuex from 'vuex';
|
||||
import mutations from './mutations';
|
||||
import state from './state';
|
||||
|
||||
export default () =>
|
||||
new Vuex.Store({
|
||||
state,
|
||||
mutations,
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';
|
|
@ -0,0 +1,7 @@
|
|||
import { SET_ERROR_MESSAGE } from './mutation_types';
|
||||
|
||||
export default {
|
||||
[SET_ERROR_MESSAGE](state, errorMessage) {
|
||||
state.errorMessage = errorMessage;
|
||||
},
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
export default () => ({
|
||||
errorMessage: undefined,
|
||||
});
|
|
@ -3,17 +3,15 @@ import NewBranchForm from '~/new_branch_form';
|
|||
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
|
||||
import initNewPipeline from '~/pipeline_new/index';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const el = document.getElementById('js-new-pipeline');
|
||||
const el = document.getElementById('js-new-pipeline');
|
||||
|
||||
if (el) {
|
||||
initNewPipeline();
|
||||
} else {
|
||||
new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new
|
||||
if (el) {
|
||||
initNewPipeline();
|
||||
} else {
|
||||
new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new
|
||||
|
||||
setupNativeFormVariableList({
|
||||
container: $('.js-ci-variable-list-section'),
|
||||
formField: 'variables_attributes',
|
||||
});
|
||||
}
|
||||
});
|
||||
setupNativeFormVariableList({
|
||||
container: $('.js-ci-variable-list-section'),
|
||||
formField: 'variables_attributes',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@
|
|||
|
||||
a {
|
||||
font-family: $monospace-font;
|
||||
display: block;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
font-size: $code-font-size !important;
|
||||
white-space: nowrap;
|
||||
|
||||
|
|
|
@ -103,14 +103,6 @@ class ApplicationController < ActionController::Base
|
|||
head :forbidden, retry_after: Gitlab::Auth::UniqueIpsLimiter.config.unique_ips_limit_time_window
|
||||
end
|
||||
|
||||
rescue_from GRPC::Unavailable, Gitlab::Git::CommandError do |exception|
|
||||
log_exception(exception)
|
||||
|
||||
headers['Retry-After'] = exception.retry_after if exception.respond_to?(:retry_after)
|
||||
|
||||
render_503
|
||||
end
|
||||
|
||||
def redirect_back_or_default(default: root_path, options: {})
|
||||
redirect_back(fallback_location: default, **options)
|
||||
end
|
||||
|
@ -246,19 +238,6 @@ class ApplicationController < ActionController::Base
|
|||
head :unprocessable_entity
|
||||
end
|
||||
|
||||
def render_503
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render(
|
||||
file: Rails.root.join("public", "503"),
|
||||
layout: false,
|
||||
status: :service_unavailable
|
||||
)
|
||||
end
|
||||
format.any { head :service_unavailable }
|
||||
end
|
||||
end
|
||||
|
||||
def no_cache_headers
|
||||
DEFAULT_GITLAB_NO_CACHE_HEADERS.each do |k, v|
|
||||
headers[k] = v
|
||||
|
|
|
@ -14,10 +14,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
# Authorize
|
||||
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
|
||||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:group_members_filtered_search, @group, default_enabled: true)
|
||||
end
|
||||
|
||||
skip_before_action :check_two_factor_requirement, only: :leave
|
||||
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
|
||||
:approve_access_request, :leave, :resend_invite,
|
||||
|
|
|
@ -94,7 +94,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
redirect_to(edit_project_path(@project, anchor: 'js-general-project-settings'))
|
||||
end
|
||||
else
|
||||
flash.now[:alert] = result[:message]
|
||||
flash[:alert] = result[:message]
|
||||
@project.reset
|
||||
|
||||
format.html { render_edit }
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
.container
|
||||
.gl-mt-3
|
||||
- if Gitlab.ee? && License.feature_available?(:devops_adoption)
|
||||
- if Gitlab.ee? && Feature.enabled?(:devops_adoption_feature, default_enabled: false) && License.feature_available?(:devops_adoption)
|
||||
= render_if_exists 'admin/dev_ops_report/devops_tabs'
|
||||
- else
|
||||
= render 'report'
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
- show_invited_members = can_manage_members && @invited_members.exists?
|
||||
- show_access_requests = can_manage_members && @requesters.exists?
|
||||
- invited_active = params[:search_invited].present? || params[:invited_members_page].present?
|
||||
- filtered_search_enabled = Feature.enabled?(:group_members_filtered_search, @group, default_enabled: true)
|
||||
- form_item_label_css_class = 'label-bold gl-mr-2 gl-mb-0 gl-py-2 align-self-md-center'
|
||||
|
||||
.js-remove-member-modal
|
||||
.project-members-page.gl-mt-3
|
||||
|
@ -51,52 +49,23 @@
|
|||
%span.badge.badge-pill= @requesters.count
|
||||
.tab-content
|
||||
#tab-members.tab-pane{ class: ('active' unless invited_active) }
|
||||
- unless filtered_search_enabled
|
||||
= render 'shared/members/tab_pane/header' do
|
||||
= render 'shared/members/tab_pane/title' do
|
||||
= html_escape(_('Members with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
|
||||
= form_tag group_group_members_path(@group), method: :get, class: 'user-search-form gl-display-flex gl-md-align-items-center gl-flex-wrap gl-flex-direction-column gl-md-flex-direction-row gl-mx-n3 gl-my-n3', data: { testid: 'user-search-form' } do
|
||||
.gl-px-3.gl-py-2
|
||||
.search-control-wrap.gl-relative
|
||||
= render 'shared/members/search_field'
|
||||
- if can_manage_members
|
||||
= render 'shared/members/tab_pane/form_item' do
|
||||
= label_tag '2fa', _('2FA'), class: form_item_label_css_class
|
||||
= render 'shared/members/filter_2fa_dropdown'
|
||||
= render 'shared/members/tab_pane/form_item' do
|
||||
= label_tag :sort_by, _('Sort by'), class: form_item_label_css_class
|
||||
= render 'shared/members/sort_dropdown'
|
||||
.js-group-members-list{ data: group_members_list_data_attributes(@group, @members) }
|
||||
.loading
|
||||
.spinner.spinner-md
|
||||
= paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil }
|
||||
- if @group.shared_with_group_links.any?
|
||||
#tab-groups.tab-pane
|
||||
- unless filtered_search_enabled
|
||||
= render 'shared/members/tab_pane/header' do
|
||||
= render 'shared/members/tab_pane/title' do
|
||||
= html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
|
||||
.js-group-linked-list{ data: linked_groups_list_data_attributes(@group) }
|
||||
.loading
|
||||
.spinner.spinner-md
|
||||
- if show_invited_members
|
||||
#tab-invited-members.tab-pane{ class: ('active' if invited_active) }
|
||||
- unless filtered_search_enabled
|
||||
= render 'shared/members/tab_pane/header' do
|
||||
= render 'shared/members/tab_pane/title' do
|
||||
= html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
|
||||
= form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do
|
||||
= render 'shared/members/search_field', name: 'search_invited'
|
||||
.js-group-invited-members-list{ data: group_members_list_data_attributes(@group, @invited_members) }
|
||||
.loading
|
||||
.spinner.spinner-md
|
||||
= paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab', params: { page: nil }
|
||||
- if show_access_requests
|
||||
#tab-access-requests.tab-pane
|
||||
- unless filtered_search_enabled
|
||||
= render 'shared/members/tab_pane/header' do
|
||||
= render 'shared/members/tab_pane/title' do
|
||||
= html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
|
||||
.js-group-access-requests-list{ data: group_members_list_data_attributes(@group, @requesters) }
|
||||
.loading
|
||||
.spinner.spinner-md
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Correctly handle Gitaly being unavailable in more locations
|
||||
merge_request: 51222
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix cut off line number in file blame
|
||||
merge_request: 51259
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove pagination from Deployment Frequency API endpoint
|
||||
merge_request: 51137
|
||||
author:
|
||||
type: changed
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: group_members_filtered_search
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48272
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/289911
|
||||
milestone: '13.7'
|
||||
name: devops_adoption_feature
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46005
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/271568
|
||||
milestone: '13.6'
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: true
|
||||
group: group::optimize
|
||||
default_enabled: false
|
|
@ -90,8 +90,8 @@ the configuration below accordingly.
|
|||
### Making local PlantUML accessible using custom GitLab setup
|
||||
|
||||
The PlantUML server runs locally on your server, so it is not accessible
|
||||
externally. As such, it is necessary to catch external PlantUML calls and
|
||||
redirect them to the local server.
|
||||
externally by default. As such, it is necessary to catch external PlantUML
|
||||
calls and redirect them to the local server.
|
||||
|
||||
The idea is to redirect each call to `https://gitlab.example.com/-/plantuml/`
|
||||
to the local PlantUML server `http://plantuml:8080/` or `http://localhost:8080/plantuml/`, depending on your setup.
|
||||
|
@ -112,6 +112,12 @@ To activate the changes, run the following command:
|
|||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
Note that the redirection through GitLab **must** be configured
|
||||
when running [GitLab with TLS](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
due to PlantUML's use of the insecure HTTP protocol. Newer browsers such
|
||||
as [Google Chrome 86+](https://www.chromestatus.com/feature/4926989725073408)
|
||||
do not load insecure HTTP resources on a page served over HTTPS.
|
||||
|
||||
### Security
|
||||
|
||||
PlantUML has features that allows fetching network resources.
|
||||
|
|
|
@ -14,6 +14,7 @@ type: reference, api
|
|||
> - `author_username` and `author_username` were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 12.10.
|
||||
> - `reference` was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20354) in GitLab 12.10 in favour of `references`.
|
||||
> - `with_merge_status_recheck` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31890) in GitLab 13.0.
|
||||
> - `reviewer_username` and `reviewer_id` were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8.
|
||||
|
||||
Every API call to merge requests must be authenticated.
|
||||
|
||||
|
@ -92,13 +93,15 @@ Parameters:
|
|||
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
|
||||
| `approver_ids` **(STARTER)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
|
||||
| `approved_by_ids` **(STARTER)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
|
||||
| `reviewer_id` | integer | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
|
||||
| `reviewer_username` | string | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. |
|
||||
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
|
||||
| `source_branch` | string | no | Return merge requests with the given source branch. |
|
||||
| `target_branch` | string | no | Return merge requests with the given target branch. |
|
||||
| `search` | string | no | Search merge requests against their `title` and `description`. |
|
||||
| `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. |
|
||||
| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests. |
|
||||
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `my_reaction_emoji`. |
|
||||
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
|
||||
| `environment` | string | no | Returns merge requests deployed to the given environment. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `deployed_before` | datetime | no | Return merge requests deployed before the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `deployed_after` | datetime | no | Return merge requests deployed after the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
|
@ -153,6 +156,14 @@ Parameters:
|
|||
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
|
||||
"web_url": "https://gitlab.example.com/axel.block"
|
||||
}],
|
||||
"reviewers": [{
|
||||
"id": 2,
|
||||
"name": "Sam Bauch",
|
||||
"username": "kenyatta_oconnell",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
|
||||
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
|
||||
}],
|
||||
"source_project_id": 2,
|
||||
"target_project_id": 3,
|
||||
"labels": [
|
||||
|
@ -268,11 +279,15 @@ Parameters:
|
|||
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
|
||||
| `approver_ids` **(STARTER)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
|
||||
| `approved_by_ids` **(STARTER)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
|
||||
| `reviewer_id` | integer | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
|
||||
| `reviewer_username` | string | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. |
|
||||
|
||||
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
|
||||
| `source_branch` | string | no | Return merge requests with the given source branch. |
|
||||
| `target_branch` | string | no | Return merge requests with the given target branch. |
|
||||
| `search` | string | no | Search merge requests against their `title` and `description`. |
|
||||
| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests. |
|
||||
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
|
||||
|
||||
```json
|
||||
[
|
||||
|
@ -324,6 +339,14 @@ Parameters:
|
|||
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
|
||||
"web_url": "https://gitlab.example.com/axel.block"
|
||||
}],
|
||||
"reviewers": [{
|
||||
"id": 2,
|
||||
"name": "Sam Bauch",
|
||||
"username": "kenyatta_oconnell",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
|
||||
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
|
||||
}],
|
||||
"source_project_id": 2,
|
||||
"target_project_id": 3,
|
||||
"labels": [
|
||||
|
@ -432,11 +455,14 @@ Parameters:
|
|||
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 9.5)_. |
|
||||
| `approver_ids` **(STARTER)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
|
||||
| `approved_by_ids` **(STARTER)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
|
||||
| `reviewer_id` | integer | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#enable-or-disable-merge-request-reviewers) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
|
||||
| `reviewer_username` | string | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#enable-or-disable-merge-request-reviewers) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. |
|
||||
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14016) in GitLab 10.0)_. |
|
||||
| `source_branch` | string | no | Return merge requests with the given source branch. |
|
||||
| `target_branch` | string | no | Return merge requests with the given target branch. |
|
||||
| `search` | string | no | Search merge requests against their `title` and `description`. |
|
||||
| `non_archived` | boolean | no | Return merge requests from non archived projects only. Default is true. _(Introduced in [GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23809))_. |
|
||||
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
|
||||
|
||||
```json
|
||||
[
|
||||
|
@ -488,6 +514,14 @@ Parameters:
|
|||
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
|
||||
"web_url": "https://gitlab.example.com/axel.block"
|
||||
}],
|
||||
"reviewers": [{
|
||||
"id": 2,
|
||||
"name": "Sam Bauch",
|
||||
"username": "kenyatta_oconnell",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
|
||||
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
|
||||
}],
|
||||
"source_project_id": 2,
|
||||
"target_project_id": 3,
|
||||
"labels": [
|
||||
|
@ -618,6 +652,14 @@ Parameters:
|
|||
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
|
||||
"web_url": "https://gitlab.example.com/axel.block"
|
||||
}],
|
||||
"reviewers": [{
|
||||
"id": 2,
|
||||
"name": "Sam Bauch",
|
||||
"username": "kenyatta_oconnell",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
|
||||
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
|
||||
}],
|
||||
"source_project_id": 2,
|
||||
"target_project_id": 3,
|
||||
"labels": [
|
||||
|
|
|
@ -38,7 +38,7 @@ collected before this feature is available.
|
|||
|
||||
## DevOps Adoption **(ULTIMATE)**
|
||||
|
||||
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247112) in GitLab 13.7.
|
||||
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247112) in GitLab 13.7 as a [Beta feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta).
|
||||
|
||||
The DevOps Adoption tab shows you which segments of your organization are using the most essential features of GitLab:
|
||||
|
||||
|
@ -61,3 +61,21 @@ DevOps Adoption allows you to:
|
|||
- Find the groups that have adopted certain features and can provide guidance to other groups on how to use those features.
|
||||
|
||||
![DevOps Report](img/dev_ops_adoption_v13_7.png)
|
||||
|
||||
### Disable or enable DevOps Adoption
|
||||
|
||||
DevOps Adoption is deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can opt to enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:devops_adoption_feature)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:devops_adoption_feature)
|
||||
```
|
||||
|
|
|
@ -215,10 +215,7 @@ To remove a member from a group:
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6.
|
||||
> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/228675) in GitLab 13.7.
|
||||
> - Improvements are [deployed behind a feature flag](../feature_flags.md), enabled by default.
|
||||
> - Improvements are enabled on GitLab.com.
|
||||
> - Improvements are recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable improvements](#enable-or-disable-improvements-to-the-ability-to-filter-and-sort-group-members). **(CORE ONLY)**
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/289911) in GitLab 13.8.
|
||||
|
||||
The following sections illustrate how you can filter and sort members in a group. To view these options,
|
||||
navigate to your desired group, go to **Members**, and include the noted search terms.
|
||||
|
@ -269,30 +266,6 @@ You can sort members by **Account**, **Access granted**, **Max role**, or **Last
|
|||
|
||||
![Group members sort](img/group_members_sort_13_7.png)
|
||||
|
||||
### Enable or disable improvements to the ability to filter and sort group members **(CORE ONLY)**
|
||||
|
||||
Group member filtering and sorting improvements are deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can opt to disable the improvements.
|
||||
|
||||
To disable them:
|
||||
|
||||
```ruby
|
||||
# For the instance
|
||||
Feature.disable(:group_members_filtered_search)
|
||||
# For a single group
|
||||
Feature.disable(:group_members_filtered_search, Group.find(<group id>))
|
||||
```
|
||||
|
||||
To enable them:
|
||||
|
||||
```ruby
|
||||
# For the instance
|
||||
Feature.enable(:group_members_filtered_search)
|
||||
# For a single group
|
||||
Feature.enable(:group_members_filtered_search, Group.find(<group id>))
|
||||
```
|
||||
|
||||
## Changing the default branch protection of a group
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7583) in GitLab 12.9.
|
||||
|
|
|
@ -21,6 +21,9 @@ module API
|
|||
coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
|
||||
desc: 'Return merge requests which are assigned to the user with the given username'
|
||||
mutually_exclusive :assignee_id, :assignee_username
|
||||
optional :reviewer_username,
|
||||
type: String,
|
||||
desc: 'Return merge requests which have the user as a reviewer with the given username'
|
||||
|
||||
optional :labels,
|
||||
type: Array[String],
|
||||
|
@ -32,6 +35,11 @@ module API
|
|||
|
||||
params :merge_requests_base_params do
|
||||
use :merge_requests_negatable_params
|
||||
optional :reviewer_id,
|
||||
types: [Integer, String],
|
||||
integer_none_any: true,
|
||||
desc: 'Return merge requests which have the user as a reviewer with the given ID'
|
||||
mutually_exclusive :reviewer_id, :reviewer_username
|
||||
optional :state,
|
||||
type: String,
|
||||
values: %w[opened closed locked merged all],
|
||||
|
@ -72,6 +80,10 @@ module API
|
|||
optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
|
||||
optional :not, type: Hash, desc: 'Parameters to negate' do
|
||||
use :merge_requests_negatable_params
|
||||
optional :reviewer_id,
|
||||
types: Integer,
|
||||
desc: 'Return merge requests which have the user as a reviewer with the given ID'
|
||||
mutually_exclusive :reviewer_id, :reviewer_username
|
||||
end
|
||||
|
||||
optional :deployed_before,
|
||||
|
|
|
@ -14114,9 +14114,6 @@ msgstr ""
|
|||
msgid "Groups with access to %{strong_open}%{project_name}%{strong_close}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Groups with access to %{strong_start}%{group_name}%{strong_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupsDropdown|Frequently visited"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17367,9 +17364,6 @@ msgstr ""
|
|||
msgid "Members can be added by project %{i_open}Maintainers%{i_close} or %{i_open}Owners%{i_close}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members invited to %{strong_start}%{group_name}%{strong_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members invited to %{strong_start}%{project_name}%{strong_end}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17388,9 +17382,6 @@ msgstr ""
|
|||
msgid "Members of a group may only view projects they have permission to access"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members with access to %{strong_start}%{group_name}%{strong_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members|%{time} by %{user}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -30911,9 +30902,6 @@ msgstr ""
|
|||
msgid "Users requesting access to"
|
||||
msgstr ""
|
||||
|
||||
msgid "Users requesting access to %{strong_start}%{group_name}%{strong_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Users requesting access to %{strong_start}%{project_name}%{strong_end}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -32938,6 +32926,9 @@ msgstr ""
|
|||
msgid "can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'"
|
||||
msgstr ""
|
||||
|
||||
msgid "can't be enabled because signed commits are required for this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be a date in the past"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -211,21 +211,6 @@ RSpec.describe ProjectsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the storage is not available', :broken_storage do
|
||||
let_it_be(:project) { create(:project, :broken_storage) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'renders a 503' do
|
||||
get :show, params: { namespace_id: project.namespace, id: project }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:service_unavailable)
|
||||
end
|
||||
end
|
||||
|
||||
context "project with empty repo" do
|
||||
let_it_be(:empty_project) { create(:project_empty_repo, :public) }
|
||||
|
||||
|
@ -616,7 +601,7 @@ RSpec.describe ProjectsController do
|
|||
expect { update_project path: 'renamed_path' }
|
||||
.not_to change { project.reload.path }
|
||||
|
||||
expect(controller).to set_flash.now[:alert].to(s_('UpdateProject|Cannot rename project because it contains container registry tags!'))
|
||||
expect(controller).to set_flash[:alert].to(s_('UpdateProject|Cannot rename project because it contains container registry tags!'))
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,174 +16,92 @@ RSpec.describe 'Groups > Members > Sort members', :js do
|
|||
sign_in(owner)
|
||||
end
|
||||
|
||||
context 'when `group_members_filtered_search` feature flag is enabled' do
|
||||
def expect_sort_by(text, sort_direction)
|
||||
within('[data-testid="members-sort-dropdown"]') do
|
||||
expect(page).to have_css('button[aria-haspopup="true"]', text: text)
|
||||
expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
|
||||
end
|
||||
end
|
||||
|
||||
it 'sorts by account by default' do
|
||||
visit_members_list(sort: nil)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
expect_sort_by('Account', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by max role ascending' do
|
||||
visit_members_list(sort: :access_level_asc)
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect_sort_by('Max role', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by max role descending' do
|
||||
visit_members_list(sort: :access_level_desc)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
expect_sort_by('Max role', :desc)
|
||||
end
|
||||
|
||||
it 'sorts by access granted ascending' do
|
||||
visit_members_list(sort: :last_joined)
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect_sort_by('Access granted', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by access granted descending' do
|
||||
visit_members_list(sort: :oldest_joined)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
expect_sort_by('Access granted', :desc)
|
||||
end
|
||||
|
||||
it 'sorts by account ascending' do
|
||||
visit_members_list(sort: :name_asc)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
expect_sort_by('Account', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by account descending' do
|
||||
visit_members_list(sort: :name_desc)
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect_sort_by('Account', :desc)
|
||||
end
|
||||
|
||||
it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
|
||||
visit_members_list(sort: :recent_sign_in)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
expect_sort_by('Last sign-in', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
|
||||
visit_members_list(sort: :oldest_sign_in)
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect_sort_by('Last sign-in', :desc)
|
||||
def expect_sort_by(text, sort_direction)
|
||||
within('[data-testid="members-sort-dropdown"]') do
|
||||
expect(page).to have_css('button[aria-haspopup="true"]', text: text)
|
||||
expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `group_members_filtered_search` feature flag is disabled' do
|
||||
dropdown_toggle_selector = '[data-testid="user-sort-dropdown"] [data-testid="dropdown-toggle"]'
|
||||
it 'sorts by account by default' do
|
||||
visit_members_list(sort: nil)
|
||||
|
||||
before do
|
||||
stub_feature_flags(group_members_filtered_search: false)
|
||||
end
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
it 'sorts alphabetically by default' do
|
||||
visit_members_list(sort: nil)
|
||||
expect_sort_by('Account', :asc)
|
||||
end
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
|
||||
end
|
||||
it 'sorts by max role ascending' do
|
||||
visit_members_list(sort: :access_level_asc)
|
||||
|
||||
it 'sorts by access level ascending' do
|
||||
visit_members_list(sort: :access_level_asc)
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, ascending')
|
||||
end
|
||||
expect_sort_by('Max role', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by access level descending' do
|
||||
visit_members_list(sort: :access_level_desc)
|
||||
it 'sorts by max role descending' do
|
||||
visit_members_list(sort: :access_level_desc)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, descending')
|
||||
end
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
it 'sorts by last joined' do
|
||||
visit_members_list(sort: :last_joined)
|
||||
expect_sort_by('Max role', :desc)
|
||||
end
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Last joined')
|
||||
end
|
||||
it 'sorts by access granted ascending' do
|
||||
visit_members_list(sort: :last_joined)
|
||||
|
||||
it 'sorts by oldest joined' do
|
||||
visit_members_list(sort: :oldest_joined)
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest joined')
|
||||
end
|
||||
expect_sort_by('Access granted', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by name ascending' do
|
||||
visit_members_list(sort: :name_asc)
|
||||
it 'sorts by access granted descending' do
|
||||
visit_members_list(sort: :oldest_joined)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
|
||||
end
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
it 'sorts by name descending' do
|
||||
visit_members_list(sort: :name_desc)
|
||||
expect_sort_by('Access granted', :desc)
|
||||
end
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Name, descending')
|
||||
end
|
||||
it 'sorts by account ascending' do
|
||||
visit_members_list(sort: :name_asc)
|
||||
|
||||
it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
|
||||
visit_members_list(sort: :recent_sign_in)
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Recent sign in')
|
||||
end
|
||||
expect_sort_by('Account', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
|
||||
visit_members_list(sort: :oldest_sign_in)
|
||||
it 'sorts by account descending' do
|
||||
visit_members_list(sort: :name_desc)
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest sign in')
|
||||
end
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect_sort_by('Account', :desc)
|
||||
end
|
||||
|
||||
it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
|
||||
visit_members_list(sort: :recent_sign_in)
|
||||
|
||||
expect(first_row.text).to include(owner.name)
|
||||
expect(second_row.text).to include(developer.name)
|
||||
|
||||
expect_sort_by('Last sign-in', :asc)
|
||||
end
|
||||
|
||||
it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
|
||||
visit_members_list(sort: :oldest_sign_in)
|
||||
|
||||
expect(first_row.text).to include(developer.name)
|
||||
expect(second_row.text).to include(owner.name)
|
||||
|
||||
expect_sort_by('Last sign-in', :desc)
|
||||
end
|
||||
|
||||
def visit_members_list(sort:)
|
||||
|
|
|
@ -87,21 +87,9 @@ describe('GroupMembersApp', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
featureFlagValue | exists
|
||||
${true} | ${true}
|
||||
${false} | ${false}
|
||||
`(
|
||||
'when `group_members_filtered_search` feature flag is $featureFlagValue',
|
||||
({ featureFlagValue, exists }) => {
|
||||
it(`${exists ? 'renders' : 'does not render'} FilterSortContainer`, () => {
|
||||
createComponent(
|
||||
{},
|
||||
{ provide: { glFeatures: { groupMembersFilteredSearch: featureFlagValue } } },
|
||||
);
|
||||
it('renders `FilterSortContainer`', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findFilterSortContainer().exists()).toBe(exists);
|
||||
});
|
||||
},
|
||||
);
|
||||
expect(findFilterSortContainer().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import JiraConnectApp from '~/jira_connect/components/app.vue';
|
||||
|
||||
describe('JiraConnectApp', () => {
|
||||
let wrapper;
|
||||
|
||||
const findHeader = () => wrapper.findByTestId('new-jira-connect-ui-heading');
|
||||
const findHeaderText = () => findHeader().text();
|
||||
|
||||
const createComponent = (options = {}) => {
|
||||
wrapper = shallowMount(JiraConnectApp, {
|
||||
provide: {
|
||||
glFeatures: { newJiraConnectUi: true },
|
||||
},
|
||||
...options,
|
||||
});
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount(JiraConnectApp, {
|
||||
provide: {
|
||||
glFeatures: { newJiraConnectUi: true },
|
||||
},
|
||||
...options,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -19,9 +24,6 @@ describe('JiraConnectApp', () => {
|
|||
wrapper = null;
|
||||
});
|
||||
|
||||
const findHeader = () => wrapper.find('h3');
|
||||
const findHeaderText = () => findHeader().text();
|
||||
|
||||
describe('template', () => {
|
||||
it('renders new UI', () => {
|
||||
createComponent();
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import mutations from '~/jira_connect/store/mutations';
|
||||
import state from '~/jira_connect/store/state';
|
||||
|
||||
describe('JiraConnect store mutations', () => {
|
||||
let localState;
|
||||
|
||||
beforeEach(() => {
|
||||
localState = state();
|
||||
});
|
||||
|
||||
describe('SET_ERROR_MESSAGE', () => {
|
||||
it('sets error message', () => {
|
||||
mutations.SET_ERROR_MESSAGE(localState, 'test error');
|
||||
|
||||
expect(localState.errorMessage).toBe('test error');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -440,6 +440,7 @@ RSpec.describe API::MergeRequests do
|
|||
milestone: milestone,
|
||||
author: user,
|
||||
assignees: [user],
|
||||
reviewers: [user2],
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
source_branch: 'what',
|
||||
|
@ -498,6 +499,71 @@ RSpec.describe API::MergeRequests do
|
|||
expect(mr['assignee']['id']).not_to eq(user2.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'filter by reviewer' do
|
||||
context 'with reviewer_id' do
|
||||
context 'with an id' do
|
||||
let(:params) { { not: { reviewer_id: user2.id } } }
|
||||
|
||||
it 'returns merge requests that do not have the given reviewer' do
|
||||
get api(endpoint_path, user), params: { not: { reviewer_id: user2.id } }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to be_an(Array)
|
||||
expect(json_response.length).to eq(4)
|
||||
expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Any' do
|
||||
let(:params) { { not: { reviewer_id: 'Any' } } }
|
||||
|
||||
it 'returns a 400' do
|
||||
# Any is not supported for negated filter
|
||||
get api(endpoint_path, user), params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['error']).to eq('not[reviewer_id] is invalid')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with None' do
|
||||
let(:params) { { not: { reviewer_id: 'None' } } }
|
||||
|
||||
it 'returns a 400' do
|
||||
# None is not supported for negated filter
|
||||
get api(endpoint_path, user), params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['error']).to eq('not[reviewer_id] is invalid')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with reviewer_username' do
|
||||
let(:params) { { not: { reviewer_username: user2.username } } }
|
||||
|
||||
it 'returns merge requests that do not have the given reviewer' do
|
||||
get api(endpoint_path, user), params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to be_an(Array)
|
||||
expect(json_response.length).to eq(4)
|
||||
expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when both reviewer_id and reviewer_username' do
|
||||
let(:params) { { not: { reviewer_id: user2.id, reviewer_username: user2.username } } }
|
||||
|
||||
it 'returns a 400' do
|
||||
get api('/merge_requests', user), params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['error']).to eq('not[reviewer_id], not[reviewer_username] are mutually exclusive')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'source_branch param' do
|
||||
|
@ -666,6 +732,79 @@ RSpec.describe API::MergeRequests do
|
|||
end
|
||||
end
|
||||
|
||||
context 'filter by reviewer' do
|
||||
let_it_be(:review_requested_mr1) do
|
||||
create(:merge_request, :unique_branches, author: user, reviewers: [user2], source_project: project2, target_project: project2)
|
||||
end
|
||||
|
||||
let_it_be(:review_requested_mr2) do
|
||||
create(:merge_request, :unique_branches, author: user2, reviewers: [user], source_project: project2, target_project: project2)
|
||||
end
|
||||
|
||||
let(:params) { { scope: :all } }
|
||||
|
||||
context 'with reviewer_id' do
|
||||
let(:params) { super().merge(reviewer_id: reviewer_id) }
|
||||
|
||||
context 'with an id' do
|
||||
let(:reviewer_id) { user2.id }
|
||||
|
||||
it 'returns review requested merge requests for the given user' do
|
||||
get api('/merge_requests', user), params: params
|
||||
|
||||
expect_response_contain_exactly(review_requested_mr1.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Any' do
|
||||
let(:reviewer_id) { 'Any' }
|
||||
|
||||
it 'returns review requested merge requests for any user' do
|
||||
get api('/merge_requests', user), params: params
|
||||
|
||||
expect_response_contain_exactly(review_requested_mr1.id, review_requested_mr2.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with None' do
|
||||
let(:reviewer_id) { 'None' }
|
||||
|
||||
it 'returns merge requests that has no assigned reviewers' do
|
||||
get api('/merge_requests', user), params: params
|
||||
|
||||
expect_response_contain_exactly(
|
||||
merge_request.id,
|
||||
merge_request_closed.id,
|
||||
merge_request_merged.id,
|
||||
merge_request_locked.id,
|
||||
merge_request2.id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with reviewer_username' do
|
||||
let(:params) { super().merge(reviewer_username: user2.username) }
|
||||
|
||||
it 'returns review requested merge requests for the given user' do
|
||||
get api('/merge_requests', user), params: params
|
||||
|
||||
expect_response_contain_exactly(review_requested_mr1.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with both reviewer_id and reviewer_username' do
|
||||
let(:params) { super().merge(reviewer_id: user2.id, reviewer_username: user2.username) }
|
||||
|
||||
it 'returns a 400' do
|
||||
get api('/merge_requests', user), params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['error']).to eq('reviewer_id, reviewer_username are mutually exclusive')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns an array of merge requests assigned to the given user' do
|
||||
merge_request3 = create(:merge_request, :simple, author: user, assignees: [user2], source_project: project2, target_project: project2, source_branch: 'other-branch')
|
||||
|
||||
|
|
|
@ -77,30 +77,6 @@ RSpec.shared_examples Repositories::GitHttpController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with exceptions' do
|
||||
before do
|
||||
allow(controller).to receive(:authenticate_user).and_return(true)
|
||||
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns 503 with GRPC Unavailable' do
|
||||
allow(controller).to receive(:access_check).and_raise(GRPC::Unavailable)
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:service_unavailable)
|
||||
end
|
||||
|
||||
it 'returns 503 with timeout error' do
|
||||
allow(controller).to receive(:access_check).and_raise(Gitlab::GitAccess::TimeoutError)
|
||||
|
||||
get :info_refs, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:service_unavailable)
|
||||
expect(response.body).to eq 'Gitlab::GitAccess::TimeoutError'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #git_upload_pack' do
|
||||
|
|
Loading…
Reference in New Issue