Commit 5c9e7778 authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-07-23' into 'master'

CE upstream - 2018-07-23 19:55 UTC

Closes #6977

See merge request gitlab-org/gitlab-ee!6633
parents 7f0c28a1 42be6581
...@@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils'; ...@@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils';
* } else { * } else {
* poll.stop(); * poll.stop();
* } * }
* }); * });
* *
* 1. Checks for response and headers before start polling * 1. Checks for response and headers before start polling
* 2. Interval is provided by `Poll-Interval` header. * 2. Interval is provided by `Poll-Interval` header.
...@@ -51,8 +51,8 @@ export default class Poll { ...@@ -51,8 +51,8 @@ export default class Poll {
constructor(options = {}) { constructor(options = {}) {
this.options = options; this.options = options;
this.options.data = options.data || {}; this.options.data = options.data || {};
this.options.notificationCallback = options.notificationCallback || this.options.notificationCallback =
function notificationCallback() {}; options.notificationCallback || function notificationCallback() {};
this.intervalHeader = 'POLL-INTERVAL'; this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null; this.timeoutID = null;
...@@ -63,6 +63,7 @@ export default class Poll { ...@@ -63,6 +63,7 @@ export default class Poll {
const headers = normalizeHeaders(response.headers); const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10); const pollInterval = parseInt(headers[this.intervalHeader], 10);
if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) { if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
clearTimeout(this.timeoutID);
this.timeoutID = setTimeout(() => { this.timeoutID = setTimeout(() => {
this.makeRequest(); this.makeRequest();
}, pollInterval); }, pollInterval);
...@@ -77,11 +78,11 @@ export default class Poll { ...@@ -77,11 +78,11 @@ export default class Poll {
notificationCallback(true); notificationCallback(true);
return resource[method](data) return resource[method](data)
.then((response) => { .then(response => {
this.checkConditions(response); this.checkConditions(response);
notificationCallback(false); notificationCallback(false);
}) })
.catch((error) => { .catch(error => {
notificationCallback(false); notificationCallback(false);
if (error.status === httpStatusCodes.ABORTED) { if (error.status === httpStatusCodes.ABORTED) {
return; return;
......
...@@ -174,27 +174,19 @@ export default { ...@@ -174,27 +174,19 @@ export default {
[types.UPDATE_NOTE](state, note) { [types.UPDATE_NOTE](state, note) {
const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id); const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
if (noteObj.individual_note) { if (noteObj.individual_note) {
noteObj.notes.splice(0, 1, note); noteObj.notes.splice(0, 1, note);
} else { } else {
const comment = utils.findNoteObjectById(noteObj.notes, note.id); const comment = utils.findNoteObjectById(noteObj.notes, note.id);
noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note); Object.assign(comment, note);
} }
}, },
[types.UPDATE_DISCUSSION](state, noteData) { [types.UPDATE_DISCUSSION](state, noteData) {
const note = noteData; const note = noteData;
let index = 0; const selectedDiscussion = state.discussions.find(n => n.id === note.id);
state.discussions.forEach((n, i) => {
if (n.id === note.id) {
index = i;
}
});
note.expanded = true; // override expand flag to prevent collapse note.expanded = true; // override expand flag to prevent collapse
state.discussions.splice(index, 1, note); Object.assign(selectedDiscussion, note);
}, },
[types.CLOSE_ISSUE](state) { [types.CLOSE_ISSUE](state) {
...@@ -215,12 +207,9 @@ export default { ...@@ -215,12 +207,9 @@ export default {
[types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) { [types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId); const discussion = utils.findNoteObjectById(state.discussions, discussionId);
const index = state.discussions.indexOf(discussion);
const discussionWithDiffLines = Object.assign({}, discussion, { Object.assign(discussion, {
truncated_diff_lines: diffLines, truncated_diff_lines: diffLines,
}); });
state.discussions.splice(index, 1, discussionWithDiffLines);
}, },
}; };
...@@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache'; ...@@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache';
const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm; const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
export const findNoteObjectById = (notes, id) => export const findNoteObjectById = (notes, id) => notes.find(n => n.id === id);
notes.filter(n => n.id === id)[0];
export const getQuickActionText = note => { export const getQuickActionText = note => {
let text = 'Applying command'; let text = 'Applying command';
const quickActions = const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
const executedCommands = quickActions.filter(command => { const executedCommands = quickActions.filter(command => {
const commandRegex = new RegExp(`/${command.name}`); const commandRegex = new RegExp(`/${command.name}`);
...@@ -29,5 +27,4 @@ export const getQuickActionText = note => { ...@@ -29,5 +27,4 @@ export const getQuickActionText = note => {
export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note); export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note);
export const stripQuickActions = note => export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim();
note.replace(REGEX_QUICK_ACTIONS, '').trim();
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
# owned: boolean # owned: boolean
# parent: Group # parent: Group
# all_available: boolean (defaults to true) # all_available: boolean (defaults to true)
# min_access_level: integer
# #
# Users with full private access can see all groups. The `owned` and `parent` # Users with full private access can see all groups. The `owned` and `parent`
# params can be used to restrict the groups that are returned. # params can be used to restrict the groups that are returned.
...@@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder ...@@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder
def all_groups def all_groups
return [owned_groups] if params[:owned] return [owned_groups] if params[:owned]
return [groups_with_min_access_level] if min_access_level?
return [Group.all] if current_user&.full_private_access? && all_available? return [Group.all] if current_user&.full_private_access? && all_available?
groups = [] groups = []
...@@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder ...@@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder
current_user.groups current_user.groups
end end
def groups_with_min_access_level
groups = current_user
.groups
.where('members.access_level >= ?', params[:min_access_level])
Gitlab::GroupHierarchy
.new(groups)
.base_and_descendants
end
def by_parent(groups) def by_parent(groups)
return groups unless params[:parent] return groups unless params[:parent]
...@@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder ...@@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder
def all_available? def all_available?
params.fetch(:all_available, true) params.fetch(:all_available, true)
end end
def min_access_level?
current_user && params[:min_access_level].present?
end
end end
class PersonalProjectsFinder < UnionFinder class PersonalProjectsFinder < UnionFinder
def initialize(user) def initialize(user, params = {})
@user = user @user = user
@params = params
end end
# Finds the projects belonging to the user in "@user", limited to either # Finds the projects belonging to the user in "@user", limited to either
...@@ -8,6 +9,8 @@ class PersonalProjectsFinder < UnionFinder ...@@ -8,6 +9,8 @@ class PersonalProjectsFinder < UnionFinder
# #
# current_user - When given the list of projects is limited to those only # current_user - When given the list of projects is limited to those only
# visible by this user. # visible by this user.
# params - Optional query parameters
# min_access_level: integer
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def execute(current_user = nil) def execute(current_user = nil)
...@@ -19,11 +22,21 @@ class PersonalProjectsFinder < UnionFinder ...@@ -19,11 +22,21 @@ class PersonalProjectsFinder < UnionFinder
private private
def all_projects(current_user) def all_projects(current_user)
projects = [] return [projects_with_min_access_level(current_user)] if current_user && min_access_level?
projects = []
projects << @user.personal_projects.visible_to_user(current_user) if current_user projects << @user.personal_projects.visible_to_user(current_user) if current_user
projects << @user.personal_projects.public_to_user(current_user) projects << @user.personal_projects.public_to_user(current_user)
projects projects
end end
def projects_with_min_access_level(current_user)
@user
.personal_projects
.visible_to_user_and_access_level(current_user, @params[:min_access_level])
end
def min_access_level?
@params[:min_access_level].present?
end
end end
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
# search: string # search: string
# non_archived: boolean # non_archived: boolean
# archived: 'only' or boolean # archived: 'only' or boolean
# min_access_level: integer
# #
class ProjectsFinder < UnionFinder class ProjectsFinder < UnionFinder
include CustomAttributesFilter include CustomAttributesFilter
...@@ -34,7 +35,7 @@ class ProjectsFinder < UnionFinder ...@@ -34,7 +35,7 @@ class ProjectsFinder < UnionFinder
user = params.delete(:user) user = params.delete(:user)
collection = collection =
if user if user
PersonalProjectsFinder.new(user).execute(current_user) PersonalProjectsFinder.new(user, finder_params).execute(current_user)
else else
init_collection init_collection
end end
...@@ -65,6 +66,8 @@ class ProjectsFinder < UnionFinder ...@@ -65,6 +66,8 @@ class ProjectsFinder < UnionFinder
def collection_with_user def collection_with_user
if owned_projects? if owned_projects?
current_user.owned_projects current_user.owned_projects
elsif min_access_level?
current_user.authorized_projects.where('project_authorizations.access_level >= ?', params[:min_access_level])
else else
if private_only? if private_only?
current_user.authorized_projects current_user.authorized_projects
...@@ -76,7 +79,7 @@ class ProjectsFinder < UnionFinder ...@@ -76,7 +79,7 @@ class ProjectsFinder < UnionFinder
# Builds a collection for an anonymous user. # Builds a collection for an anonymous user.
def collection_without_user def collection_without_user
if private_only? || owned_projects? if private_only? || owned_projects? || min_access_level?
Project.none Project.none
else else
Project.public_to_user Project.public_to_user
...@@ -91,6 +94,10 @@ class ProjectsFinder < UnionFinder ...@@ -91,6 +94,10 @@ class ProjectsFinder < UnionFinder
params[:non_public].present? params[:non_public].present?
end end
def min_access_level?
params[:min_access_level].present?
end
def by_ids(items) def by_ids(items)
project_ids_relation ? items.where(id: project_ids_relation) : items project_ids_relation ? items.where(id: project_ids_relation) : items
end end
...@@ -143,4 +150,10 @@ class ProjectsFinder < UnionFinder ...@@ -143,4 +150,10 @@ class ProjectsFinder < UnionFinder
projects projects
end end
end end
def finder_params
return {} unless min_access_level?
{ min_access_level: params[:min_access_level] }
end
end end
...@@ -334,6 +334,7 @@ class Project < ActiveRecord::Base ...@@ -334,6 +334,7 @@ class Project < ActiveRecord::Base
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) } scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) } scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) } scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
scope :archived, -> { where(archived: true) } scope :archived, -> { where(archived: true) }
scope :non_archived, -> { where(archived: false) } scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
......
...@@ -7,4 +7,4 @@ ...@@ -7,4 +7,4 @@
= link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer" = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer"
.col-lg-9 .col-lg-9
= render_if_exists 'projects/services/prometheus/metrics', project: @project = render 'projects/services/prometheus/metrics', project: @project
---
title: Add filter for minimal access level in groups and projects API
merge_request: 20478
author: Marko, Peter
type: added
--- ---
title: Reduces the client side memory footprint on merge requests title: Reduces the client side memory footprint on merge requests
merge_request: 20744 merge_request: 20744
author: author:
type: performance type: performance
...@@ -10,13 +10,14 @@ Parameters: ...@@ -10,13 +10,14 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `skip_groups` | array of integers | no | Skip the group IDs passed | | `skip_groups` | array of integers | no | Skip the group IDs passed |
| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | | `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria | | `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) | | `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `owned` | boolean | no | Limit to groups owned by the current user | | `owned` | boolean | no | Limit to groups explicitly owned by the current user |
| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
``` ```
GET /groups GET /groups
...@@ -94,13 +95,14 @@ Parameters: ...@@ -94,13 +95,14 @@ Parameters:
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
| `skip_groups` | array of integers | no | Skip the group IDs passed | | `skip_groups` | array of integers | no | Skip the group IDs passed |
| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | | `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria | | `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) | | `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `owned` | boolean | no | Limit to groups owned by the current user | | `owned` | boolean | no | Limit to groups explicitly owned by the current user |
| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
``` ```
GET /groups/:id/subgroups GET /groups/:id/subgroups
......
...@@ -48,7 +48,7 @@ GET /projects ...@@ -48,7 +48,7 @@ GET /projects
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria | | `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `owned` | boolean | no | Limit by projects owned by the current user | | `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of | | `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user | | `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics | | `statistics` | boolean | no | Include project statistics |
...@@ -57,6 +57,7 @@ GET /projects ...@@ -57,6 +57,7 @@ GET /projects
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ | | `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
| `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ | | `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
When `simple=true` or the user is unauthenticated this returns something like: When `simple=true` or the user is unauthenticated this returns something like:
...@@ -275,13 +276,14 @@ GET /users/:user_id/projects ...@@ -275,13 +276,14 @@ GET /users/:user_id/projects
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria | | `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `owned` | boolean | no | Limit by projects owned by the current user | | `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of | | `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user | | `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics | | `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```json ```json
[ [
...@@ -781,13 +783,14 @@ GET /projects/:id/forks ...@@ -781,13 +783,14 @@ GET /projects/:id/forks
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria | | `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `owned` | boolean | no | Limit by projects owned by the current user | | `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of | | `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user | | `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics | | `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```bash ```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks" curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks"
......
...@@ -43,11 +43,12 @@ module API ...@@ -43,11 +43,12 @@ module API
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id' optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
use :pagination use :pagination
end end
def find_groups(params, parent_id = nil) def find_groups(params, parent_id = nil)
find_params = params.slice(:all_available, :custom_attributes, :owned) find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
find_params[:parent] = find_group!(parent_id) if parent_id find_params[:parent] = find_group!(parent_id) if parent_id
find_params[:all_available] = find_params[:all_available] =
find_params.fetch(:all_available, current_user&.full_private_access?) find_params.fetch(:all_available, current_user&.full_private_access?)
......
...@@ -402,6 +402,7 @@ module API ...@@ -402,6 +402,7 @@ module API
finder_params[:search] = params[:search] if params[:search] finder_params[:search] = params[:search] if params[:search]
finder_params[:user] = params.delete(:user) if params[:user] finder_params[:user] = params.delete(:user) if params[:user]
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes] finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
finder_params finder_params
end end
......
...@@ -56,6 +56,7 @@ module API ...@@ -56,6 +56,7 @@ module API
optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of' optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of'
optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature' optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature' optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
use :optional_filter_params_ee use :optional_filter_params_ee
end end
......
...@@ -42,6 +42,7 @@ module API ...@@ -42,6 +42,7 @@ module API
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups' optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user' optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
optional :avatar, type: File, desc: 'Avatar image for user' optional :avatar, type: File, desc: 'Avatar image for user'
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
all_or_none_of :extern_uid, :provider all_or_none_of :extern_uid, :provider
# EE # EE
......
...@@ -240,6 +240,25 @@ describe API::Groups do ...@@ -240,6 +240,25 @@ describe API::Groups do
expect(json_response.first['name']).to eq(group2.name) expect(json_response.first['name']).to eq(group2.name)
end end
end end
context 'when using min_access_level in the request' do
let!(:group3) { create(:group, :private) }
let(:response_groups) { json_response.map { |group| group['id'] } }
before do
group1.add_developer(user2)
group3.add_master(user2)
end
it 'returns an array of groups the user has at least master access' do
get api('/groups', user2), min_access_level: 40
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(response_groups).to eq([group2.id, group3.id])
end
end
end end
describe "GET /groups/:id" do describe "GET /groups/:id" do
......
...@@ -400,6 +400,22 @@ describe API::Projects do ...@@ -400,6 +400,22 @@ describe API::Projects do
end end
end end
end end
context 'and with min_access_level' do
before do
project2.add_master(user2)
project3.add_developer(user2)
project4.add_reporter(user2)
end
it 'returns an array of groups the user has at least developer access' do
get api('/projects', user2), { min_access_level: 30 }
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(project2.id, project3.id)
end
end
end end
context 'when authenticated as a different user' do context 'when authenticated as a different user' do
...@@ -681,6 +697,20 @@ describe API::Projects do ...@@ -681,6 +697,20 @@ describe API::Projects do
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id) expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end end
it 'returns projects filetered by minimal access level' do
private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace)
private_project1.add_developer(user2)
private_project2.add_reporter(user2)
get api("/users/#{user4.id}/projects/", user2), { min_access_level: 30 }
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(private_project1.id)
end
end end
describe 'POST /projects/user/:id' do describe 'POST /projects/user/:id' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment