Commit bd1a5a9f authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge branch 'remove-nested-groups-checks' into 'master'

Remove code related to object hierarchy and MySQL

Closes #65056 and #65055

See merge request gitlab-org/gitlab-ce!31095
parents 900ef6fc 1ce5bcac
......@@ -421,7 +421,7 @@ class ApplicationController < ActionController::Base
end
def manifest_import_enabled?
Group.supports_nested_objects? && Gitlab::CurrentSettings.import_sources.include?('manifest')
Gitlab::CurrentSettings.import_sources.include?('manifest')
end
def phabricator_import_enabled?
......
......@@ -32,18 +32,14 @@ module GroupTree
def filtered_groups_with_ancestors(groups)
filtered_groups = groups.search(params[:filter]).page(params[:page])
if Group.supports_nested_objects?
# We find the ancestors by ID of the search results here.
# Otherwise the ancestors would also have filters applied,
# which would cause them not to be preloaded.
#
# Pagination needs to be applied before loading the ancestors to
# make sure ancestors are not cut off by pagination.
Gitlab::ObjectHierarchy.new(Group.where(id: filtered_groups.select(:id)))
.base_and_ancestors
else
filtered_groups
end
# We find the ancestors by ID of the search results here.
# Otherwise the ancestors would also have filters applied,
# which would cause them not to be preloaded.
#
# Pagination needs to be applied before loading the ancestors to
# make sure ancestors are not cut off by pagination.
Gitlab::ObjectHierarchy.new(Group.where(id: filtered_groups.select(:id)))
.base_and_ancestors
end
# rubocop: enable CodeReuse/ActiveRecord
end
......@@ -132,8 +132,6 @@ class GroupDescendantsFinder
end
def subgroups
return Group.none unless Group.supports_nested_objects?
# When filtering subgroups, we want to find all matches within the tree of
# descendants to show to the user
groups = if params[:filter]
......
......@@ -14,10 +14,8 @@ module Types
group.avatar_url(only_path: false)
end
if ::Group.supports_nested_objects?
field :parent, GroupType,
null: true,
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find }
end
field :parent, GroupType,
null: true,
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find }
end
end
......@@ -127,10 +127,6 @@ module GroupsHelper
groups.to_json
end
def supports_nested_groups?
Group.supports_nested_objects?
end
private
def get_group_sidebar_links
......
# frozen_string_literal: true
module Descendant
extend ActiveSupport::Concern
class_methods do
def supports_nested_objects?
Gitlab::Database.postgresql?
end
end
end
......@@ -10,7 +10,6 @@ class Group < Namespace
include Referable
include SelectForProjectAuthorization
include LoadedInGroupList
include Descendant
include GroupDescendant
include TokenAuthenticatable
include WithUploads
......
......@@ -332,8 +332,6 @@ class Namespace < ApplicationRecord
end
def force_share_with_group_lock_on_descendants
return unless Group.supports_nested_objects?
# We can't use `descendants.update_all` since Rails will throw away the WITH
# RECURSIVE statement. We also can't use WHERE EXISTS since we can't use
# different table aliases, hence we're just using WHERE IN. Since we have a
......
......@@ -16,8 +16,6 @@ class GroupPolicy < BasePolicy
condition(:maintainer) { access_level >= GroupMember::MAINTAINER }
condition(:reporter) { access_level >= GroupMember::REPORTER }
condition(:nested_groups_supported, scope: :global) { Group.supports_nested_objects? }
condition(:has_parent, scope: :subject) { @subject.has_parent? }
condition(:share_with_group_locked, scope: :subject) { @subject.share_with_group_lock? }
condition(:parent_share_with_group_locked, scope: :subject) { @subject.parent&.share_with_group_lock? }
......@@ -108,8 +106,8 @@ class GroupPolicy < BasePolicy
enable :read_nested_project_resources
end
rule { owner & nested_groups_supported }.enable :create_subgroup
rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup
rule { owner }.enable :create_subgroup
rule { maintainer & maintainer_can_create_group }.enable :create_subgroup
rule { public_group | logged_in_viewable }.enable :view_globally
......
......@@ -18,10 +18,6 @@ module Groups
return namespace
end
if group_path.include?('/') && !Group.supports_nested_objects?
raise 'Nested groups are not supported on MySQL'
end
create_group_path
end
......
......@@ -43,7 +43,6 @@ module Groups
def ensure_allowed_transfer
raise_transfer_error(:group_is_already_root) if group_is_already_root?
raise_transfer_error(:database_not_supported) unless Group.supports_nested_objects?
raise_transfer_error(:same_parent_as_current) if same_parent?
raise_transfer_error(:invalid_policies) unless valid_policies?
raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
......
......@@ -31,7 +31,7 @@ module Members
return unless member.is_a?(GroupMember) && member.user && member.group
delete_project_members(member)
delete_subgroup_members(member) if Group.supports_nested_objects?
delete_subgroup_members(member)
end
def delete_project_members(member)
......
......@@ -102,13 +102,7 @@ module Users
end
def fresh_authorizations
klass = if Group.supports_nested_objects?
Gitlab::ProjectAuthorizations::WithNestedGroups
else
Gitlab::ProjectAuthorizations::WithoutNestedGroups
end
klass.new(user).calculate
Gitlab::ProjectAuthorizations.new(user).calculate
end
end
end
......@@ -23,20 +23,19 @@
= f.submit 'Change group path', class: 'btn btn-warning'
- if supports_nested_groups?
.sub-section
%h4.warning-title Transfer group
= form_for @group, url: transfer_group_path(@group), method: :put do |f|
.form-group
= dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group) } })
= hidden_field_tag 'new_parent_group_id'
.sub-section
%h4.warning-title Transfer group
= form_for @group, url: transfer_group_path(@group), method: :put do |f|
.form-group
= dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group) } })
= hidden_field_tag 'new_parent_group_id'
%ul
%li Be careful. Changing a group's parent can have unintended #{link_to 'side effects', 'https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths', target: 'blank'}.
%li You can only transfer the group to a group you manage.
%li You will need to update your local repositories to point to the new location.
%li If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.
= f.submit 'Transfer group', class: 'btn btn-warning'
%ul
%li Be careful. Changing a group's parent can have unintended #{link_to 'side effects', 'https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths', target: 'blank'}.
%li You can only transfer the group to a group you manage.
%li You will need to update your local repositories to point to the new location.
%li If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.
= f.submit 'Transfer group', class: 'btn btn-warning'
.sub-section
%h4.danger-title= _('Remove group')
......
......@@ -366,10 +366,7 @@ module API
end
expose :request_access_enabled
expose :full_name, :full_path
if ::Group.supports_nested_objects?
expose :parent_id
end
expose :parent_id
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
......
......@@ -114,10 +114,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the group'
requires :path, type: String, desc: 'The path of the group'
if ::Group.supports_nested_objects?
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
end
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
use :optional_params
end
......
......@@ -32,11 +32,6 @@ module Gitlab
# Returns the maximum depth starting from the base
# A base object with no children has a maximum depth of `1`
def max_descendants_depth
unless hierarchy_supported?
# This makes the return value consistent with the case where hierarchy is supported
return descendants_base.exists? ? 1 : nil
end
base_and_descendants(with_depth: true).maximum(DEPTH_COLUMN)
end
......@@ -66,8 +61,6 @@ module Gitlab
# each parent.
# rubocop: disable CodeReuse/ActiveRecord
def base_and_ancestors(upto: nil, hierarchy_order: nil)
return ancestors_base unless hierarchy_supported?
recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all)
recursive_query = recursive_query.order(depth: hierarchy_order) if hierarchy_order
......@@ -81,10 +74,6 @@ module Gitlab
# When `with_depth` is `true`, a `depth` column is included where it starts with `1` for the base objects
# and incremented as we go down the descendant tree
def base_and_descendants(with_depth: false)
unless hierarchy_supported?
return with_depth ? descendants_base.select("1 as #{DEPTH_COLUMN}", objects_table[Arel.star]) : descendants_base
end
read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(model.all))
end
......@@ -112,8 +101,6 @@ module Gitlab
# If nested objects are not supported, ancestors_base is returned.
# rubocop: disable CodeReuse/ActiveRecord
def all_objects
return ancestors_base unless hierarchy_supported?
ancestors = base_and_ancestors_cte
descendants = base_and_descendants_cte
......@@ -135,10 +122,6 @@ module Gitlab
private
def hierarchy_supported?
Gitlab::Database.postgresql?
end
# rubocop: disable CodeReuse/ActiveRecord
def base_and_ancestors_cte(stop_id = nil, hierarchy_order = nil)
cte = SQL::RecursiveCTE.new(:base_and_ancestors)
......
# frozen_string_literal: true
# This class relies on Common Table Expressions to efficiently get all data,
# including data for nested groups.
module Gitlab
class ProjectAuthorizations
attr_reader :user
# user - The User object for which to calculate the authorizations.
def initialize(user)
@user = user
end
def calculate
cte = recursive_cte
cte_alias = cte.table.alias(Group.table_name)
projects = Project.arel_table
links = ProjectGroupLink.arel_table
relations = [
# The project a user has direct access to.
user.projects.select_for_project_authorization,
# The personal projects of the user.
user.personal_projects.select_as_maintainer_for_project_authorization,
# Projects that belong directly to any of the groups the user has
# access to.
Namespace
.unscoped
.select([alias_as_column(projects[:id], 'project_id'),
cte_alias[:access_level]])
.from(cte_alias)
.joins(:projects),
# Projects shared with any of the namespaces the user has access to.
Namespace
.unscoped
.select([
links[:project_id],
least(cte_alias[:access_level], links[:group_access], 'access_level')
])
.from(cte_alias)
.joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id')
.joins('INNER JOIN projects ON projects.id = project_group_links.project_id')
.joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id')
.where('p_ns.share_with_group_lock IS FALSE')
]
ProjectAuthorization
.unscoped
.with
.recursive(cte.to_arel)
.select_from_union(relations)
end
private
# Builds a recursive CTE that gets all the groups the current user has
# access to, including any nested groups.
def recursive_cte
cte = Gitlab::SQL::RecursiveCTE.new(:namespaces_cte)
members = Member.arel_table
namespaces = Namespace.arel_table
# Namespaces the user is a member of.
cte << user.groups
.select([namespaces[:id], members[:access_level]])
.except(:order)
# Sub groups of any groups the user is a member of.
cte << Group.select([
namespaces[:id],
greatest(members[:access_level], cte.table[:access_level], 'access_level')
])
.joins(join_cte(cte))
.joins(join_members)
.except(:order)
cte
end
# Builds a LEFT JOIN to join optional memberships onto the CTE.
def join_members
members = Member.arel_table
namespaces = Namespace.arel_table
cond = members[:source_id]
.eq(namespaces[:id])
.and(members[:source_type].eq('Namespace'))
.and(members[:requested_at].eq(nil))
.and(members[:user_id].eq(user.id))
Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
end
# Builds an INNER JOIN to join namespaces onto the CTE.
def join_cte(cte)
namespaces = Namespace.arel_table
cond = cte.table[:id].eq(namespaces[:parent_id])
Arel::Nodes::InnerJoin.new(cte.table, Arel::Nodes::On.new(cond))
end
def greatest(left, right, column_alias)
sql_function('GREATEST', [left, right], column_alias)
end
def least(left, right, column_alias)
sql_function('LEAST', [left, right], column_alias)
end
def sql_function(name, args, column_alias)
alias_as_column(Arel::Nodes::NamedFunction.new(name, args), column_alias)
end
def alias_as_column(value, alias_to)
Arel::Nodes::As.new(value, Arel::Nodes::SqlLiteral.new(alias_to))
end
end
end
# frozen_string_literal: true
module Gitlab
module ProjectAuthorizations
# Calculating new project authorizations when supporting nested groups.
#
# This class relies on Common Table Expressions to efficiently get all data,
# including data for nested groups. As a result this class can only be used
# on PostgreSQL.
class WithNestedGroups
attr_reader :user
# user - The User object for which to calculate the authorizations.
def initialize(user)
@user = user
end
def calculate
cte = recursive_cte
cte_alias = cte.table.alias(Group.table_name)
projects = Project.arel_table
links = ProjectGroupLink.arel_table
relations = [
# The project a user has direct access to.
user.projects.select_for_project_authorization,
# The personal projects of the user.
user.personal_projects.select_as_maintainer_for_project_authorization,
# Projects that belong directly to any of the groups the user has
# access to.
Namespace
.unscoped
.select([alias_as_column(projects[:id], 'project_id'),
cte_alias[:access_level]])
.from(cte_alias)
.joins(:projects),
# Projects shared with any of the namespaces the user has access to.
Namespace
.unscoped
.select([links[:project_id],
least(cte_alias[:access_level],
links[:group_access],
'access_level')])
.from(cte_alias)
.joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id')
.joins('INNER JOIN projects ON projects.id = project_group_links.project_id')
.joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id')
.where('p_ns.share_with_group_lock IS FALSE')
]
ProjectAuthorization
.unscoped
.with
.recursive(cte.to_arel)
.select_from_union(relations)
end
private
# Builds a recursive CTE that gets all the groups the current user has
# access to, including any nested groups.
def recursive_cte
cte = Gitlab::SQL::RecursiveCTE.new(:namespaces_cte)
members = Member.arel_table
namespaces = Namespace.arel_table
# Namespaces the user is a member of.
cte << user.groups
.select([namespaces[:id], members[:access_level]])
.except(:order)
# Sub groups of any groups the user is a member of.
cte << Group.select([namespaces[:id],
greatest(members[:access_level],
cte.table[:access_level], 'access_level')])
.joins(join_cte(cte))
.joins(join_members)
.except(:order)
cte
end
# Builds a LEFT JOIN to join optional memberships onto the CTE.
def join_members
members = Member.arel_table
namespaces = Namespace.arel_table
cond = members[:source_id]
.eq(namespaces[:id])
.and(members[:source_type].eq('Namespace'))
.and(members[:requested_at].eq(nil))
.and(members[:user_id].eq(user.id))
Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
end
# Builds an INNER JOIN to join namespaces onto the CTE.
def join_cte(cte)
namespaces = Namespace.arel_table
cond = cte.table[:id].eq(namespaces[:parent_id])
Arel::Nodes::InnerJoin.new(cte.table, Arel::Nodes::On.new(cond))
end
def greatest(left, right, column_alias)
sql_function('GREATEST', [left, right], column_alias)
end
def least(left, right, column_alias)
sql_function('LEAST', [left, right], column_alias)
end
def sql_function(name, args, column_alias)
alias_as_column(Arel::Nodes::NamedFunction.new(name, args), column_alias)
end
def alias_as_column(value, alias_to)
Arel::Nodes::As.new(value, Arel::Nodes::SqlLiteral.new(alias_to))
end
end
end
end
# frozen_string_literal: true
module Gitlab
module ProjectAuthorizations
# Calculating new project authorizations when not supporting nested groups.
class WithoutNestedGroups
attr_reader :user
# user - The User object for which to calculate the authorizations.
def initialize(user)
@user = user
end
def calculate
relations = [
# Projects the user is a direct member of
user.projects.select_for_project_authorization,
# Personal projects
user.personal_projects.select_as_maintainer_for_project_authorization,
# Projects of groups the user is a member of
user.groups_projects.select_for_project_authorization,
# Projects shared with groups the user is a member of
user.groups.joins(:shared_projects).select_for_project_authorization
]
ProjectAuthorization
.unscoped
.select_from_union(relations)
end
end
end
end
......@@ -85,7 +85,7 @@ describe Boards::IssuesController do
expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1))
end
it 'avoids N+1 database queries when adding a subgroup, project, and issue', :nested_groups do
it 'avoids N+1 database queries when adding a subgroup, project, and issue' do
create(:project, group: sub_group_1)
create(:labeled_issue, project: project, labels: [development])
control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count
......
......@@ -30,7 +30,7 @@ describe GroupTree do
expect(assigns(:groups)).to contain_exactly(other_group)
end
context 'for subgroups', :nested_groups do
context 'for subgroups' do
it 'only renders root groups when no parent was given' do
create(:group, :public, parent: group)
......@@ -85,7 +85,7 @@ describe GroupTree do
expect(json_response.first['id']).to eq(group.id)
end
context 'nested groups', :nested_groups do
context 'nested groups' do
it 'expands the tree when filtering' do
subgroup = create(:group, :public, parent: group, name: 'filter')
......
......@@ -26,7 +26,7 @@ describe Dashboard::GroupsController do
expect(assigns(:groups)).to contain_exactly(member_of_group)
end
context 'when rendering an expanded hierarchy with public groups you are not a member of', :nested_groups do
context 'when rendering an expanded hierarchy with public groups you are not a member of' do
let!(:top_level_result) { create(:group, name: 'chef-top') }
let!(:top_level_a) { create(:group, name: 'top-a') }
let!(:sub_level_result_a) { create(:group, name: 'chef-sub-a', parent: top_level_a) }
......
......@@ -46,7 +46,7 @@ describe Groups::ChildrenController do
end
end
context 'for subgroups', :nested_groups do
context 'for subgroups' do
let!(:public_subgroup) { create(:group, :public, parent: group) }
let!(:private_subgroup) { create(:group, :private, parent: group) }
let!(:public_project) { create(:project, :public, namespace: group) }
......@@ -292,7 +292,7 @@ describe Groups::ChildrenController do
end
end
context 'with subgroups and projects', :nested_groups do
context 'with subgroups and projects' do
let!(:first_page_subgroups) { create_list(:group, per_page, :public, parent: group) }
let!(:other_subgroup) { create(:group, :public, parent: group) }
let!(:next_page_projects) { create_list(:project, per_page, :public, namespace: group) }
......
......@@ -24,7 +24,7 @@ describe Groups::LabelsController do
expect(label_ids).to match_array([label_1.title, group_label_1.title])
end
context 'with ancestor group', :nested_groups do
context 'with ancestor group' do
set(:subgroup) { create(:group, parent: group) }
set(:subgroup_label_1) { create(:group_label, group: subgroup, title: 'subgroup_label_1') }
......@@ -32,7 +32,7 @@ describe Groups::LabelsController do
subgroup.add_owner(user)
end
it 'returns ancestor group labels', :nested_groups do
it 'returns ancestor group labels' do
get :index, params: { group_id: subgroup, include_ancestor_groups: true, only_group_labels: true }, format: :json
label_ids = json_response.map {|label| label['title']}
......
......@@ -89,7 +89,7 @@ describe GroupsController do
end
describe 'GET #new' do
context 'when creating subgroups', :nested_groups do
context 'when creating subgroups' do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
before do
......@@ -166,7 +166,7 @@ describe GroupsController do
end
end
context 'when creating subgroups', :nested_groups do
context 'when creating subgroups' do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
context 'and logged in as Owner' do
......@@ -584,7 +584,7 @@ describe GroupsController do
end
end
describe 'PUT transfer', :postgresql do
describe 'PUT transfer' do
before do
sign_in(user)
end
......
......@@ -231,7 +231,7 @@ describe Import::BitbucketController do
end
end
context 'user has chosen an existing nested namespace and name for the project', :postgresql do
context 'user has chosen an existing nested namespace and name for the project' do
let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
let(:test_name) { 'test_name' }
......@@ -250,7 +250,7 @@ describe Import::BitbucketController do
end
end
context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do
context 'user has chosen a non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
......@@ -281,7 +281,7 @@ describe Import::BitbucketController do
end
end
context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
context 'user has chosen existent and non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo') }
......
......@@ -197,7 +197,7 @@ describe Import::GitlabController do
end
end
context 'user has chosen an existing nested namespace for the project', :postgresql do
context 'user has chosen an existing nested namespace for the project' do
let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
......@@ -215,7 +215,7 @@ describe Import::GitlabController do
end
end
context 'user has chosen a non-existent nested namespaces for the project', :postgresql do
context 'user has chosen a non-existent nested namespaces for the project' do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
......@@ -246,7 +246,7 @@ describe Import::GitlabController do
end
end
context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
context 'user has chosen existent and non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo') }
......
......@@ -115,7 +115,7 @@ describe Projects::MilestonesController do
end
end
context 'with nested groups', :nested_groups do
context 'with nested groups' do
let!(:subgroup) { create(:group, :public, parent: group) }
let!(:subgroup_milestone) { create(:milestone, group: subgroup) }
......
......@@ -27,7 +27,7 @@ describe 'Dashboard Groups page', :js do
expect(page).not_to have_content(another_group.name)
end
it 'shows subgroups the user is member of', :nested_groups do
it 'shows subgroups the user is member of' do
group.add_owner(user)
nested_group.add_owner(user)
......@@ -40,7 +40,7 @@ describe 'Dashboard Groups page', :js do
expect(page).to have_content(nested_group.name)
end
context 'when filtering groups', :nested_groups do
context 'when filtering groups' do
before do
group.add_owner(user)
nested_group.add_owner(user)
......@@ -79,7 +79,7 @@ describe 'Dashboard Groups page', :js do
end
end
context 'with subgroups', :nested_groups do
context 'with subgroups' do
let!(:subgroup) { create(:group, :public, parent: group) }
before do
......
......@@ -110,7 +110,7 @@ describe 'Group empty states' do
end
context 'group without a project' do
context 'group has a subgroup', :nested_groups do
context 'group has a subgroup' do
let(:subgroup) { create(:group, parent: group) }
let(:subgroup_project) { create(:project, namespace: subgroup) }
......
......@@ -121,7 +121,7 @@ describe 'Edit group settings' do
expect(find(:css, '.group-root-path').text).to eq(root_url)
end
it 'has a parent group URL label for a subgroup group', :postgresql do
it 'has a parent group URL label for a subgroup group' do
subgroup = create(:group, parent: group)
visit edit_group_path(subgroup)
......
......@@ -50,7 +50,7 @@ describe 'Group issues page' do
end
end
context 'issues list', :nested_groups do
context 'issues list' do
let(:subgroup) { create(:group, parent: group) }
let(:subgroup_project) { create(:project, :public, group: subgroup)}
let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
......
......@@ -13,7 +13,7 @@ describe 'Groups > Members > List members' do
sign_in(user1)
end
it 'show members from current group and parent', :nested_groups do
it 'show members from current group and parent' do
group.add_developer(user1)
nested_group.add_developer(user2)
......@@ -23,7 +23,7 @@ describe 'Groups > Members > List members' do
expect(second_row.text).to include(user2.name)
end
it 'show user once if member of both current group and parent', :nested_groups do
it 'show user once if member of both current group and parent' do
group.add_developer(user1)
nested_group.add_developer(user1)
......
......@@ -9,7 +9,7 @@ describe 'Group share with group lock' do
sign_in(root_owner)
end
context 'with a subgroup', :nested_groups do
context 'with a subgroup' do
let!(:subgroup) { create(:group, parent: root_group) }
context 'when enabling the parent group share with group lock' do
......
......@@ -75,11 +75,7 @@ describe 'Group show page' do
sign_in(owner)
end
context 'when subgroups are supported', :nested_groups do
before do
allow(Group).to receive(:supports_nested_objects?) { true }
end
context 'when subgroups are supported' do
it 'allows creating subgroups' do
visit path
......@@ -87,19 +83,6 @@ describe 'Group show page' do
.to have_css("li[data-text='New subgroup']", visible: false)
end
end
context 'when subgroups are not supported' do
before do
allow(Group).to receive(:supports_nested_objects?) { false }
end
it 'does not allow creating subgroups' do
visit path
expect(page)
.not_to have_selector("li[data-text='New subgroup']", visible: false)
end
end
end
context 'for maintainers' do
......@@ -107,11 +90,7 @@ describe 'Group show page' do
sign_in(maintainer)
end
context 'when subgroups are supported', :nested_groups do
before do
allow(Group).to receive(:supports_nested_objects?) { true }
end
context 'when subgroups are supported' do
context 'when subgroup_creation_level is set to maintainers' do
before do
relaxed_group.add_maintainer(maintainer)
......@@ -141,19 +120,6 @@ describe 'Group show page' do
end
end
end
context 'when subgroups are not supported' do
before do
allow(Group).to receive(:supports_nested_objects?) { false }
end
it 'does not allow creating subgroups' do
visit path
expect(page)
.not_to have_selector("li[data-text='New subgroup']", visible: false)
end
end
end
end
......
......@@ -105,7 +105,7 @@ describe 'Group' do
end
end
describe 'create a nested group', :nested_groups, :js do
describe 'create a nested group', :js do
let(:group) { create(:group, path: 'foo') }
context 'as admin' do
......@@ -231,7 +231,7 @@ describe 'Group' do
end
end
describe 'group page with nested groups', :nested_groups, :js do
describe 'group page with nested groups', :js do
let!(:group) { create(:group) }
let!(:nested_group) { create(:group, parent: group) }
let!(:project) { create(:project, namespace: group) }
......
require 'spec_helper'
describe 'IDE', :js do
describe 'sub-groups', :nested_groups do
describe 'sub-groups' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
......
require 'spec_helper'
describe 'Import multiple repositories by uploading a manifest file', :js, :postgresql do
describe 'Import multiple repositories by uploading a manifest file', :js do
include Select2Helper
let(:user) { create(:admin) }
......
require 'spec_helper'
describe 'Labels Hierarchy', :js, :nested_groups do
describe 'Labels Hierarchy', :js do
include FilteredSearchHelpers
let!(:user) { create(:user) }
......
......@@ -58,7 +58,7 @@ describe 'Project > Members > Invite group', :js do
end
end
context 'for a project in a subgroup', :nested_groups do
context 'for a project in a subgroup' do
let!(:group_to_share_with) { create(:group) }
let(:root_group) { create(:group) }
let(:subgroup) { create(:group, parent: root_group) }
......@@ -181,7 +181,7 @@ describe 'Project > Members > Invite group', :js do
group_to_share_with.add_maintainer(maintainer)
end
it 'the groups dropdown does not show ancestors', :nested_groups do
it 'the groups dropdown does not show ancestors' do
visit project_settings_members_path(project)
click_on 'invite-group-tab'
......
......@@ -294,7 +294,7 @@ describe 'New project' do
end
end
context 'from manifest file', :postgresql do
context 'from manifest file' do
before do
first('.import_manifest').click
end
......
......@@ -126,7 +126,7 @@ describe "Projects > Settings > Pipelines settings" do
end
end
context 'when auto devops is turned on group parent level', :nested_groups do
context 'when auto devops is turned on group parent level' do
before do
group = create(:group, parent: create(:group, :auto_devops_enabled))
project.update!(namespace: group)
......
......@@ -68,7 +68,7 @@ describe 'Projects > Settings > User transfers a project', :js do
end
end
context 'when nested groups are available', :nested_groups do
context 'when nested groups are available' do
it 'allows transferring a project to a subgroup' do
subgroup = create(:group, parent: group)
......
require 'spec_helper'
describe 'Subgroup Issuables', :js, :nested_groups do
describe 'Subgroup Issuables', :js do
let!(:group) { create(:group, name: 'group') }
let!(:subgroup) { create(:group, parent: group, name: 'subgroup') }
let!(:project) { create(:project, namespace: subgroup, name: 'project') }
......
......@@ -26,7 +26,7 @@ describe 'User creates a project', :js do
expect(page).to have_content(project.url_to_repo)
end
context 'in a subgroup they do not own', :nested_groups do
context 'in a subgroup they do not own' do
let(:parent) { create(:group) }
let!(:subgroup) { create(:group, parent: parent) }
......
......@@ -314,7 +314,7 @@ describe 'Project' do
end
end
context 'for subgroups', :js, :nested_groups do
context 'for subgroups', :js do
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
let(:project) { create(:project, :repository, group: subgroup) }
......
......@@ -50,7 +50,7 @@ describe Autocomplete::UsersFinder do
it { is_expected.to match_array([user1]) }
end
context 'when passed a subgroup', :nested_groups do
context 'when passed a subgroup' do
let(:grandparent) { create(:group, :public) }
let(:parent) { create(:group, :public, parent: grandparent) }
let(:child) { create(:group, :public, parent: parent) }
......
......@@ -32,7 +32,7 @@ describe ClusterAncestorsFinder, '#execute' do
is_expected.to eq([project_cluster, group_cluster, instance_cluster])
end
context 'nested groups', :nested_groups do
context 'nested groups' do
let(:group) { create(:group, parent: parent_group) }
let(:parent_group) { create(:group) }
......@@ -65,7 +65,7 @@ describe ClusterAncestorsFinder, '#execute' do
is_expected.to eq([group_cluster, instance_cluster])
end
context 'nested groups', :nested_groups do
context 'nested groups' do
let(:group) { create(:group, parent: parent_group) }
let(:parent_group) { create(:group) }
......
......@@ -19,7 +19,7 @@ describe GroupDescendantsFinder do
expect(finder.has_children?).to be_truthy
end
context 'when there are subgroups', :nested_groups do
context 'when there are subgroups' do
it 'is true when there are projects' do
create(:group, parent: group)
......@@ -99,7 +99,7 @@ describe GroupDescendantsFinder do
)
end
context 'with nested groups', :nested_groups do
context 'with nested groups' do
let!(:subgroup1) { create(:group, parent: group, name: 'a', path: 'sub-a') }
let!(:subgroup2) { create(:group, parent: group, name: 'z', path: 'sub-z') }
......@@ -126,7 +126,7 @@ describe GroupDescendantsFinder do
end
end
context 'with nested groups', :nested_groups do
context 'with nested groups' do
let!(:project) { create(:project, namespace: group) }
let!(:subgroup) { create(:group, :private, parent: group) }
......
......@@ -18,7 +18,7 @@ describe GroupMembersFinder, '#execute' do
expect(result.to_a).to match_array([member3, member2, member1])
end
it 'returns members for nested group', :nested_groups do
it 'returns members for nested group' do
group.add_developer(user2)
nested_group.request_access(user4)
member1 = group.add_maintainer(user1)
......@@ -30,7 +30,7 @@ describe GroupMembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member3, member4])
end
it 'returns members for descendant groups if requested', :nested_groups do
it 'returns members for descendant groups if requested' do
member1 = group.add_maintainer(user2)
member2 = group.add_maintainer(user1)
nested_group.add_maintainer(user2)
......
......@@ -19,7 +19,7 @@ describe GroupProjectsFinder do
context "only owned" do
let(:options) { { only_owned: true } }
context 'with subgroups projects', :nested_groups do
context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
......@@ -33,7 +33,7 @@ describe GroupProjectsFinder do
end
context "all" do
context 'with subgroups projects', :nested_groups do
context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
......@@ -78,7 +78,7 @@ describe GroupProjectsFinder do
subgroup_private_project.add_maintainer(current_user)
end
context 'with subgroups projects', :nested_groups do
context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
......@@ -96,7 +96,7 @@ describe GroupProjectsFinder do
current_user.update(external: true)
end
context 'with subgroups projects', :nested_groups do
context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
......@@ -111,7 +111,7 @@ describe GroupProjectsFinder do
end
context "all" do
context 'with subgroups projects', :nested_groups do
context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
......@@ -153,7 +153,7 @@ describe GroupProjectsFinder do
context "only owned" do
let(:options) { { only_owned: true } }
context 'with subgroups projects', :nested_groups do
context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
......
......@@ -65,7 +65,7 @@ describe GroupsFinder do
end
end
context 'subgroups', :nested_groups do
context 'subgroups' do
let(:user) { create(:user) }
let!(:parent_group) { create(:group, :public) }
let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
......
......@@ -51,7 +51,7 @@ describe IssuesFinder do
end
end
context 'when include_subgroup param is true', :nested_groups do
context 'when include_subgroup param is true' do
before do
params[:include_subgroups] = true
end
......
......@@ -89,7 +89,7 @@ describe LabelsFinder do
end
end
context 'when including labels from group ancestors', :nested_groups do
context 'when including labels from group ancestors' do
it 'returns labels from group and its ancestors' do
private_group_1.add_developer(user)
private_subgroup_1.add_developer(user)
......@@ -108,7 +108,7 @@ describe LabelsFinder do
end
end
context 'when including labels from group descendants', :nested_groups do
context 'when including labels from group descendants' do
it 'returns labels from group and its descendants' do
private_group_1.add_developer(user)
private_subgroup_1.add_developer(user)
......@@ -128,7 +128,7 @@ describe LabelsFinder do
end
end
context 'filtering by project_id', :nested_groups do
context 'filtering by project_id' do
context 'when include_ancestor_groups is true' do
let!(:sub_project) { create(:project, namespace: private_subgroup_1 ) }
let!(:project_label) { create(:label, project: sub_project, title: 'Label 5') }
......
......@@ -9,7 +9,7 @@ describe MembersFinder, '#execute' do
set(:user3) { create(:user) }
set(:user4) { create(:user) }
it 'returns members for project and parent groups', :nested_groups do
it 'returns members for project and parent groups' do
nested_group.request_access(user1)
member1 = group.add_maintainer(user2)
member2 = nested_group.add_maintainer(user3)
......@@ -20,7 +20,7 @@ describe MembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member2, member3])
end
it 'includes nested group members if asked', :nested_groups do
it 'includes nested group members if asked' do
project = create(:project, namespace: group)
nested_group.request_access(user1)
member1 = group.add_maintainer(user2)
......@@ -32,7 +32,7 @@ describe MembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member2, member3])
end
context 'when include_invited_groups_members == true', :nested_groups do
context 'when include_invited_groups_members == true' do
subject { described_class.new(project, user2).execute(include_invited_groups_members: true) }
set(:linked_group) { create(:group, :public, :access_requestable) }
......@@ -40,7 +40,7 @@ describe MembersFinder, '#execute' do
set(:linked_group_member) { linked_group.add_developer(user1) }
set(:nested_linked_group_member) { nested_linked_group.add_developer(user2) }
it 'includes all the invited_groups members including members inherited from ancestor groups', :nested_groups do
it 'includes all the invited_groups members including members inherited from ancestor groups' do
create(:project_group_link, project: project, group: nested_linked_group)
expect(subject).to contain_exactly(linked_group_member, nested_linked_group_member)
......
......@@ -42,7 +42,7 @@ describe MergeRequestsFinder do
expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
end
it 'filters by group including subgroups', :nested_groups do
it 'filters by group including subgroups' do
params = { group_id: group.id, include_subgroups: true }
merge_requests = described_class.new(user, params).execute
......
......@@ -36,7 +36,7 @@ describe TodosFinder do
expect(todos).to match_array([todo1])
end
context 'with subgroups', :nested_groups do
context 'with subgroups' do
let(:subgroup) { create(:group, parent: group) }
let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) }
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe Resolvers::NamespaceProjectsResolver, :nested_groups do
describe Resolvers::NamespaceProjectsResolver do
include GraphqlHelpers
let(:current_user) { create(:user) }
......
......@@ -118,7 +118,7 @@ describe AutoDevopsHelper do
it { is_expected.to eq('instance enabled') }
end
context 'with groups', :nested_groups do
context 'with groups' do
before do
receiver.update(parent: parent)
end
......@@ -170,7 +170,7 @@ describe AutoDevopsHelper do
it { is_expected.to eq('instance enabled') }
end
context 'with groups', :nested_groups do
context 'with groups' do
let(:receiver) { create(:project, :repository, namespace: group) }
before do
......@@ -203,7 +203,7 @@ describe AutoDevopsHelper do
it { is_expected.to be_nil }
end
context 'with groups', :nested_groups do
context 'with groups' do
let(:receiver) { create(:project, :repository, namespace: group) }
context 'when auto devops is disabled on group level' do
......
......@@ -86,7 +86,7 @@ describe GroupsHelper do
end
end
describe 'group_title', :nested_groups do
describe 'group_title' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:deep_nested_group) { create(:group, parent: nested_group) }
......@@ -99,7 +99,7 @@ describe GroupsHelper do
end
# rubocop:disable Layout/SpaceBeforeComma
describe '#share_with_group_lock_help_text', :nested_groups do
describe '#share_with_group_lock_help_text' do
let!(:root_group) { create(:group) }
let!(:subgroup) { create(:group, parent: root_group) }
let!(:sub_subgroup) { create(:group, parent: subgroup) }
......@@ -230,7 +230,7 @@ describe GroupsHelper do
end
end
describe 'parent_group_options', :nested_groups do
describe 'parent_group_options' do
let(:current_user) { create(:user) }
let(:group) { create(:group, name: 'group') }
let(:group2) { create(:group, name: 'group2') }
......
require 'spec_helper'
describe NamespacesHelper, :postgresql do
describe NamespacesHelper do
let!(:admin) { create(:admin) }
let!(:admin_project_creation_level) { nil }
let!(:admin_group) do
......@@ -109,7 +109,7 @@ describe NamespacesHelper, :postgresql do
expect(options).to include(user_group.name)
end
context 'when nested groups are available', :nested_groups do
context 'when nested groups are available' do
it 'includes groups nested in groups the user can administer' do
allow(helper).to receive(:current_user).and_return(user)
child_group = create(:group, :private, parent: user_group)
......
......@@ -363,7 +363,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
expect(doc.css('a')).to be_empty
end
it 'supports parent group references', :nested_groups do
it 'supports parent group references' do
milestone.update!(group: parent_group)
doc = reference_filter("See #{reference}")
......@@ -396,7 +396,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
context 'when group milestone' do
let(:group_milestone) { create(:milestone, title: 'group_milestone', group: group) }
context 'for subgroups', :nested_groups do
context 'for subgroups' do
let(:sub_group) { create(:group, parent: group) }
let(:sub_group_milestone) { create(:milestone, title: 'sub_group_milestone', group: sub_group) }
......
......@@ -75,7 +75,7 @@ describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, sch
create_resource(target_type, 1, 2)
end
it 'ignores label links referencing ancestor group labels', :nested_groups do
it 'ignores label links referencing ancestor group labels' do
labels_table.create(id: 4, title: 'bug', color: 'red', project_id: 2, type: 'ProjectLabel')
label_links_table.create(label_id: 4, target_type: target_type, target_id: 1)
link = label_links_table.create(label_id: 1, target_type: target_type, target_id: 1)
......@@ -85,7 +85,7 @@ describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, sch
expect(link.reload.label_id).to eq(1)
end
it 'checks also issues and MRs in subgroups', :nested_groups do
it 'checks also issues and MRs in subgroups' do
link = label_links_table.create(label_id: 2, target_type: target_type, target_id: 1)
subject.perform(1, 100)
......
......@@ -103,7 +103,7 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
end
end
context 'with subgroups', :nested_groups do
context 'with subgroups' do
let(:project_path) { 'a-group/a-sub-group/a-project' }
let(:existing_group) do
......@@ -188,20 +188,6 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
end
end
context 'when subgroups are not available' do
let(:project_path) { 'a-group/a-sub-group/a-project' }
before do
expect(Group).to receive(:supports_nested_objects?) { false }
end
describe '#create_project_if_needed' do
it 'raises an error' do
expect { importer.create_project_if_needed }.to raise_error('Nested groups are not supported on MySQL')
end
end
end
def prepare_repository(project_path, source_project)
repo_path = File.join(base_dir, project_path)
......
......@@ -8,7 +8,7 @@ describe Gitlab::Database::Count::ReltuplesCountStrategy do
subject { described_class.new(models).count }
describe '#count', :postgresql do
describe '#count' do
let(:models) { [Project, Identity] }
context 'when reltuples is up to date' do
......
......@@ -12,7 +12,7 @@ describe Gitlab::Database::Count::TablesampleCountStrategy do
subject { strategy.count }
describe '#count', :postgresql do
describe '#count' do
let(:estimates) do
{
Project => threshold + 1,
......
......@@ -8,7 +8,7 @@ describe Gitlab::Database::Grant do
expect(described_class.create_and_execute_trigger?('users')).to eq(true)
end
it 'returns false when the user can not create and/or execute a trigger', :postgresql do
it 'returns false when the user can not create and/or execute a trigger' do
# In case of MySQL the user may have SUPER permissions, making it
# impossible to have `false` returned when running tests; hence we only
# run these tests on PostgreSQL.
......
......@@ -20,7 +20,7 @@ describe Gitlab::GroupSearchResults do
expect(result).to eq [user1]
end
it 'returns the user belonging to the subgroup matching the search query', :nested_groups do
it 'returns the user belonging to the subgroup matching the search query' do
user1 = create(:user, username: 'gob_bluth')
subgroup = create(:group, parent: group)
create(:group_member, :developer, user: user1, group: subgroup)
......@@ -32,7 +32,7 @@ describe Gitlab::GroupSearchResults do
expect(result).to eq [user1]
end
it 'returns the user belonging to the parent group matching the search query', :nested_groups do
it 'returns the user belonging to the parent group matching the search query' do
user1 = create(:user, username: 'gob_bluth')
parent_group = create(:group, children: [group])
create(:group_member, :developer, user: user1, group: parent_group)
......@@ -44,7 +44,7 @@ describe Gitlab::GroupSearchResults do
expect(result).to eq [user1]
end
it 'does not return the user belonging to the private subgroup', :nested_groups do
it 'does not return the user belonging to the private subgroup' do
user1 = create(:user, username: 'gob_bluth')
subgroup = create(:group, :private, parent: group)
create(:group_member, :developer, user: user1, group: subgroup)
......
require 'spec_helper'
describe Gitlab::ManifestImport::Manifest, :postgresql do
describe Gitlab::ManifestImport::Manifest do
let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) }
let(:manifest) { described_class.new(file) }
......
require 'spec_helper'
describe Gitlab::ManifestImport::ProjectCreator, :postgresql do
describe Gitlab::ManifestImport::ProjectCreator do
let(:group) { create(:group) }
let(:user) { create(:user) }
let(:repository) do
......
require 'spec_helper'
describe Gitlab::ObjectHierarchy, :postgresql do
describe Gitlab::ObjectHierarchy do
let!(:parent) { create(:group) }
let!(:child1) { create(:group, parent: parent) }
let!(:child2) { create(:group, parent: child1) }
......
......@@ -96,7 +96,7 @@ describe Gitlab::PerformanceBar do
end
end
context 'when allowed group is nested', :nested_groups do
context 'when allowed group is nested' do
let!(:nested_my_group) { create(:group, parent: create(:group, path: 'my-org'), path: 'my-group') }
before do
......@@ -110,7 +110,7 @@ describe Gitlab::PerformanceBar do
end
end
context 'when a nested group has the same path', :nested_groups do
context 'when a nested group has the same path' do
before do
create(:group, :nested, path: 'my-group').add_developer(user)
end
......
......@@ -20,13 +20,7 @@ describe Gitlab::ProjectAuthorizations do
end
let(:authorizations) do
klass = if Group.supports_nested_objects?
Gitlab::ProjectAuthorizations::WithNestedGroups
else
Gitlab::ProjectAuthorizations::WithoutNestedGroups
end
klass.new(user).calculate
described_class.new(user).calculate
end
it 'returns the correct number of authorizations' do
......@@ -46,28 +40,26 @@ describe Gitlab::ProjectAuthorizations do
expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
if Group.supports_nested_objects?
context 'with nested groups' do
let!(:nested_group) { create(:group, parent: group) }
let!(:nested_project) { create(:project, namespace: nested_group) }
context 'with nested groups' do
let!(:nested_group) { create(:group, parent: group) }
let!(:nested_project) { create(:project, namespace: nested_group) }
it 'includes nested groups' do
expect(authorizations.pluck(:project_id)).to include(nested_project.id)
end
it 'includes nested groups' do
expect(authorizations.pluck(:project_id)).to include(nested_project.id)
end
it 'inherits access levels when the user is not a member of a nested group' do
mapping = map_access_levels(authorizations)
it 'inherits access levels when the user is not a member of a nested group' do
mapping = map_access_levels(authorizations)
expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
it 'uses the greatest access level when a user is a member of a nested group' do
nested_group.add_maintainer(user)
it 'uses the greatest access level when a user is a member of a nested group' do
nested_group.add_maintainer(user)
mapping = map_access_levels(authorizations)
mapping = map_access_levels(authorizations)
expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
end
require 'spec_helper'
describe Gitlab::SQL::CTE, :postgresql do
describe Gitlab::SQL::CTE do
describe '#to_arel' do
it 'generates an Arel relation for the CTE body' do
relation = User.where(id: 1)
......
require 'spec_helper'
describe Gitlab::SQL::RecursiveCTE, :postgresql do
describe Gitlab::SQL::RecursiveCTE do
let(:cte) { described_class.new(:cte_name) }
describe '#to_arel' do
......
......@@ -146,7 +146,7 @@ describe Ci::Runner do
expect(described_class.belonging_to_parent_group_of_project(project.id)).to contain_exactly(runner)
end
context 'with a parent group with a runner', :nested_groups do
context 'with a parent group with a runner' do
let(:runner) { create(:ci_runner, :group, groups: [parent_group]) }
let(:project) { create(:project, group: group) }
let(:group) { create(:group, parent: parent_group) }
......
......@@ -342,7 +342,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
context 'when sub-group has configured kubernetes cluster', :nested_groups do
context 'when sub-group has configured kubernetes cluster' do
let(:sub_group_cluster) { create(:cluster, :provided_by_gcp, :group) }
let(:sub_group) { sub_group_cluster.group }
let(:project) { create(:project, group: sub_group) }
......
......@@ -45,7 +45,7 @@ describe DeploymentPlatform do
is_expected.to eq(group_cluster.platform_kubernetes)
end
context 'when child group has configured kubernetes cluster', :nested_groups do
context 'when child group has configured kubernetes cluster' do
let(:child_group1) { create(:group, parent: group) }
let!(:child_group1_cluster) { create(:cluster_for_group, groups: [child_group1]) }
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe GroupDescendant, :nested_groups do
describe GroupDescendant do
let(:parent) { create(:group) }
let(:subgroup) { create(:group, parent: parent) }
let(:subsub_group) { create(:group, parent: subgroup) }
......@@ -84,7 +84,7 @@ describe GroupDescendant, :nested_groups do
it 'tracks the exception when a parent was not preloaded' do
expect(Gitlab::Sentry).to receive(:track_exception).and_call_original
expect { GroupDescendant.build_hierarchy([subsub_group]) }.to raise_error(ArgumentError)
expect { described_class.build_hierarchy([subsub_group]) }.to raise_error(ArgumentError)
end
it 'recovers if a parent was not reloaded by querying for the parent' do
......@@ -93,7 +93,7 @@ describe GroupDescendant, :nested_groups do
# this does not raise in production, so stubbing it here.
allow(Gitlab::Sentry).to receive(:track_exception)
expect(GroupDescendant.build_hierarchy([subsub_group])).to eq(expected_hierarchy)
expect(described_class.build_hierarchy([subsub_group])).to eq(expected_hierarchy)
end
it 'raises an error if not all elements were preloaded' do
......
......@@ -128,7 +128,7 @@ describe Issuable do
expect(build_issuable(milestone.id).milestone_available?).to be_truthy
end
it 'returns true with a milestone from the the parent of the issue project group', :nested_groups do
it 'returns true with a milestone from the the parent of the issue project group' do
parent = create(:group)
group.update(parent: parent)
milestone = create(:milestone, group: parent)
......
......@@ -71,7 +71,7 @@ describe Group do
end
end
describe '#notification_settings', :nested_groups do
describe '#notification_settings' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:sub_group) { create(:group, parent_id: group.id) }
......@@ -237,7 +237,7 @@ describe Group do
it { is_expected.to match_array([private_group, internal_group, group]) }
end
context 'when user is a member of private subgroup', :postgresql do
context 'when user is a member of private subgroup' do
let!(:private_subgroup) { create(:group, :private, parent: private_group) }
before do
......@@ -416,7 +416,7 @@ describe Group do
it { expect(group.last_owner?(@members[:owner])).to be_falsy }
end
context 'with owners from a parent', :postgresql do
context 'with owners from a parent' do
before do
parent_group = create(:group)
create(:group_member, :owner, group: parent_group)
......@@ -524,7 +524,7 @@ describe Group do
it { expect(subject.parent).to be_kind_of(described_class) }
end
describe '#members_with_parents', :nested_groups do
describe '#members_with_parents' do
let!(:group) { create(:group, :nested) }
let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
......@@ -535,7 +535,7 @@ describe Group do
end
end
describe '#direct_and_indirect_members', :nested_groups do
describe '#direct_and_indirect_members' do
let!(:group) { create(:group, :nested) }
let!(:sub_group) { create(:group, parent: group) }
let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
......@@ -552,7 +552,7 @@ describe Group do
end
end
describe '#users_with_descendants', :nested_groups do
describe '#users_with_descendants' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
......@@ -571,7 +571,7 @@ describe Group do
end
end
describe '#direct_and_indirect_users', :nested_groups do
describe '#direct_and_indirect_users' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
let(:user_c) { create(:user) }
......@@ -601,7 +601,7 @@ describe Group do
end
end
describe '#project_users_with_descendants', :nested_groups do
describe '#project_users_with_descendants' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
let(:user_c) { create(:user) }
......@@ -678,7 +678,7 @@ describe Group do
end
end
context 'sub groups and projects', :nested_groups do
context 'sub groups and projects' do
it 'enables two_factor_requirement for group member' do
group.add_user(user, GroupMember::OWNER)
......@@ -687,7 +687,7 @@ describe Group do
expect(user.reload.require_two_factor_authentication_from_group).to be_truthy
end
context 'expanded group members', :nested_groups do
context 'expanded group members' do
let(:indirect_user) { create(:user) }
it 'enables two_factor_requirement for subgroup member' do
......@@ -720,7 +720,7 @@ describe Group do
expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
it 'does not enable two_factor_requirement for subgroup child project member', :nested_groups do
it 'does not enable two_factor_requirement for subgroup child project member' do
subgroup = create(:group, :nested, parent: group)
project = create(:project, group: subgroup)
project.add_maintainer(user)
......@@ -820,7 +820,7 @@ describe Group do
it_behaves_like 'ref is protected'
end
context 'when group has children', :postgresql do
context 'when group has children' do
let(:group_child) { create(:group, parent: group) }
let(:group_child_2) { create(:group, parent: group_child) }
let(:group_child_3) { create(:group, parent: group_child_2) }
......@@ -843,7 +843,7 @@ describe Group do
end
end
describe '#highest_group_member', :nested_groups do
describe '#highest_group_member' do
let(:nested_group) { create(:group, parent: group) }
let(:nested_group_2) { create(:group, parent: nested_group) }
let(:user) { create(:user) }
......@@ -932,7 +932,7 @@ describe Group do
it { is_expected.to eq(config) }
end
context 'with parent groups', :nested_groups do
context 'with parent groups' do
where(:instance_value, :parent_value, :group_value, :config) do
# Instance level enabled
true | nil | nil | { status: true, scope: :instance }
......
......@@ -69,7 +69,7 @@ describe GroupMember do
end
end
context 'access levels', :nested_groups do
context 'access levels' do
context 'with parent group' do
it_behaves_like 'inherited access level as a member of entity' do
let(:entity) { create(:group, parent: parent_entity) }
......
......@@ -130,7 +130,7 @@ describe ProjectMember do
end
end
context 'with parent group and a subgroup', :nested_groups do
context 'with parent group and a subgroup' do
it_behaves_like 'inherited access level as a member of entity' do
let(:subgroup) { create(:group, parent: parent_entity) }
let(:entity) { create(:project, group: subgroup) }
......
......@@ -56,7 +56,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
it_behaves_like 'data refresh'
context 'with subgroups', :nested_groups do
context 'with subgroups' do
let(:subgroup1) { create(:group, parent: namespace)}
let(:subgroup2) { create(:group, parent: subgroup1)}
......
......@@ -191,7 +191,7 @@ describe Namespace do
end
end
describe '#ancestors_upto', :nested_groups do
describe '#ancestors_upto' do
let(:parent) { create(:group) }
let(:child) { create(:group, parent: parent) }
let(:child2) { create(:group, parent: child) }
......@@ -271,7 +271,7 @@ describe Namespace do
end
end
context 'with subgroups', :nested_groups do
context 'with subgroups' do
let(:parent) { create(:group, name: 'parent', path: 'parent') }
let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') }
let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
......@@ -475,7 +475,7 @@ describe Namespace do
end
end
describe '#self_and_hierarchy', :nested_groups do
describe '#self_and_hierarchy' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
......@@ -490,7 +490,7 @@ describe Namespace do
end
end
describe '#ancestors', :nested_groups do
describe '#ancestors' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:deep_nested_group) { create(:group, parent: nested_group) }
......@@ -504,7 +504,7 @@ describe Namespace do
end
end
describe '#self_and_ancestors', :nested_groups do
describe '#self_and_ancestors' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:deep_nested_group) { create(:group, parent: nested_group) }
......@@ -518,7 +518,7 @@ describe Namespace do
end
end
describe '#descendants', :nested_groups do
describe '#descendants' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
......@@ -534,7 +534,7 @@ describe Namespace do
end
end
describe '#self_and_descendants', :nested_groups do
describe '#self_and_descendants' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
......@@ -550,7 +550,7 @@ describe Namespace do
end
end
describe '#users_with_descendants', :nested_groups do
describe '#users_with_descendants' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
......@@ -597,7 +597,7 @@ describe Namespace do
it { expect(group.all_pipelines.to_a).to match_array([pipeline1, pipeline2]) }
end
describe '#share_with_group_lock with subgroups', :nested_groups do
describe '#share_with_group_lock with subgroups' do
context 'when creating a subgroup' do
let(:subgroup) { create(:group, parent: root_group )}
......@@ -738,7 +738,7 @@ describe Namespace do
end
describe '#root_ancestor' do
it 'returns the top most ancestor', :nested_groups do
it 'returns the top most ancestor' do
root_group = create(:group)
nested_group = create(:group, parent: root_group)
deep_nested_group = create(:group, parent: nested_group)
......
......@@ -49,7 +49,7 @@ describe NotificationRecipient do
end
context '#notification_setting' do
context 'for child groups', :nested_groups do
context 'for child groups' do
let!(:moved_group) { create(:group) }
let(:group) { create(:group) }
let(:sub_group_1) { create(:group, parent: group) }
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe Postgresql::ReplicationSlot, :postgresql do
describe Postgresql::ReplicationSlot do
describe '.in_use?' do
it 'returns true when replication slots are present' do
expect(described_class).to receive(:exists?).and_return(true)
......
......@@ -25,7 +25,7 @@ describe ProjectGroupLink do
expect(project_group_link).not_to be_valid
end
it "doesn't allow a project to be shared with an ancestor of the group it is in", :nested_groups do
it "doesn't allow a project to be shared with an ancestor of the group it is in" do
project_group_link.group = parent_group
expect(project_group_link).not_to be_valid
......
......@@ -2263,7 +2263,7 @@ describe Project do
end
end
describe '#ancestors_upto', :nested_groups do
describe '#ancestors_upto' do
let(:parent) { create(:group) }
let(:child) { create(:group, parent: parent) }
let(:child2) { create(:group, parent: child) }
......@@ -2302,7 +2302,7 @@ describe Project do
it { is_expected.to eq(group) }
end
context 'in a nested group', :nested_groups do
context 'in a nested group' do
let(:root) { create(:group) }
let(:child) { create(:group, parent: root) }
let(:project) { create(:project, group: child) }
......@@ -2450,7 +2450,7 @@ describe Project do
expect(forked_project.in_fork_network_of?(project)).to be_truthy
end
it 'is true for a fork of a fork', :postgresql do
it 'is true for a fork of a fork' do
other_fork = fork_project(forked_project)
expect(other_fork.in_fork_network_of?(project)).to be_truthy
......@@ -3772,7 +3772,7 @@ describe Project do
end
end
context 'when enabled on root parent', :nested_groups do
context 'when enabled on root parent' do
let(:parent_group) { create(:group, parent: create(:group, :auto_devops_enabled)) }
context 'when auto devops instance enabled' do
......@@ -3792,7 +3792,7 @@ describe Project do
end
end
context 'when disabled on root parent', :nested_groups do
context 'when disabled on root parent' do
let(:parent_group) { create(:group, parent: create(:group, :auto_devops_disabled)) }
context 'when auto devops instance enabled' do
......@@ -4235,18 +4235,16 @@ describe Project do
expect(project.badges.count).to eq 3
end
if Group.supports_nested_objects?
context 'with nested_groups' do
let(:parent_group) { create(:group) }
context 'with nested_groups' do
let(:parent_group) { create(:group) }
before do
create_list(:group_badge, 2, group: project_group)
project_group.update(parent: parent_group)
end
before do
create_list(:group_badge, 2, group: project_group)
project_group.update(parent: parent_group)
end
it 'returns the project and the project nested groups badges' do
expect(project.badges.count).to eq 5
end
it 'returns the project and the project nested groups badges' do
expect(project.badges.count).to eq 5
end
end
end
......
......@@ -262,11 +262,7 @@ describe Todo do
todo2 = create(:todo, group: child_group)
todos = described_class.for_group_and_descendants(parent_group)
expect(todos).to include(todo1)
# Nested groups only work on PostgreSQL, so on MySQL todo2 won't be
# present.
expect(todos).to include(todo2) if Gitlab::Database.postgresql?
expect(todos).to contain_exactly(todo1, todo2)
end
end
......
......@@ -985,7 +985,7 @@ describe User do
it { expect(user.namespaces).to contain_exactly(user.namespace, group) }
it { expect(user.manageable_namespaces).to contain_exactly(user.namespace, group) }
context 'with child groups', :nested_groups do
context 'with child groups' do
let!(:subgroup) { create(:group, parent: group) }
describe '#manageable_namespaces' do
......@@ -2082,11 +2082,7 @@ describe User do
subject { user.membership_groups }
if Group.supports_nested_objects?
it { is_expected.to contain_exactly parent_group, child_group }
else
it { is_expected.to contain_exactly parent_group }
end
it { is_expected.to contain_exactly parent_group, child_group }
end
describe '#authorizations_for_projects' do
......@@ -2386,7 +2382,7 @@ describe User do
it_behaves_like :member
end
context 'with subgroup with different owner for project runner', :nested_groups do
context 'with subgroup with different owner for project runner' do
let(:group) { create(:group) }
let(:another_user) { create(:user) }
let(:subgroup) { create(:group, parent: group) }
......@@ -2490,22 +2486,16 @@ describe User do
group.add_owner(user)
end
if Group.supports_nested_objects?
it 'returns all groups' do
is_expected.to match_array [
group,
nested_group_1, nested_group_1_1,
nested_group_2, nested_group_2_1
]
end
else
it 'returns the top-level groups' do
is_expected.to match_array [group]
end
it 'returns all groups' do
is_expected.to match_array [
group,
nested_group_1, nested_group_1_1,
nested_group_2, nested_group_2_1
]
end
end
context 'user is member of the first child (internal node), branch 1', :nested_groups do
context 'user is member of the first child (internal node), branch 1' do
before do
nested_group_1.add_owner(user)
end
......@@ -2518,7 +2508,7 @@ describe User do
end
end
context 'user is member of the first child (internal node), branch 2', :nested_groups do
context 'user is member of the first child (internal node), branch 2' do
before do
nested_group_2.add_owner(user)
end
......@@ -2531,7 +2521,7 @@ describe User do
end
end
context 'user is member of the last child (leaf node)', :nested_groups do
context 'user is member of the last child (leaf node)' do
before do
nested_group_1_1.add_owner(user)
end
......@@ -2687,7 +2677,7 @@ describe User do
end
end
context 'with 2FA requirement from expanded groups', :nested_groups do
context 'with 2FA requirement from expanded groups' do
let!(:group1) { create :group, require_two_factor_authentication: true }
let!(:group1a) { create :group, parent: group1 }
......@@ -2702,7 +2692,7 @@ describe User do
end
end
context 'with 2FA requirement on nested child group', :nested_groups do
context 'with 2FA requirement on nested child group' do
let!(:group1) { create :group, require_two_factor_authentication: false }
let!(:group1a) { create :group, require_two_factor_authentication: true, parent: group1 }
......
......@@ -58,7 +58,7 @@ describe GroupMemberPolicy do
end
end
context 'with the group parent', :postgresql do
context 'with the group parent' do
let(:current_user) { create :user }
let(:subgroup) { create(:group, :private, parent: group)}
......
......@@ -51,7 +51,7 @@ describe GroupPolicy do
it { expect_allowed(:read_label, :read_list) }
context 'in subgroups', :nested_groups do
context 'in subgroups' do
let(:subgroup) { create(:group, :private, parent: group) }
let(:project) { create(:project, namespace: subgroup) }
......@@ -104,8 +104,6 @@ describe GroupPolicy do
end
it 'allows every maintainer permission plus creating subgroups' do
allow(Group).to receive(:supports_nested_objects?).and_return(true)
create_subgroup_permission = [:create_subgroup]
updated_maintainer_permissions =
maintainer_permissions + create_subgroup_permission
......@@ -122,8 +120,6 @@ describe GroupPolicy do
context 'with subgroup_creation_level set to owner' do
it 'allows every maintainer permission' do
allow(Group).to receive(:supports_nested_objects?).and_return(true)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
......@@ -137,8 +133,6 @@ describe GroupPolicy do
let(:current_user) { owner }
it do
allow(Group).to receive(:supports_nested_objects?).and_return(true)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
......@@ -151,8 +145,6 @@ describe GroupPolicy do
let(:current_user) { admin }
it do
allow(Group).to receive(:supports_nested_objects?).and_return(true)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
......@@ -161,52 +153,7 @@ describe GroupPolicy do
end
end
describe 'when nested group support feature is disabled' do
before do
allow(Group).to receive(:supports_nested_objects?).and_return(false)
end
context 'admin' do
let(:current_user) { admin }
it 'allows every owner permission except creating subgroups' do
create_subgroup_permission = [:create_subgroup]
updated_owner_permissions =
owner_permissions - create_subgroup_permission
expect_disallowed(*create_subgroup_permission)
expect_allowed(*updated_owner_permissions)
end
end
context 'owner' do
let(:current_user) { owner }
it 'allows every owner permission except creating subgroups' do
create_subgroup_permission = [:create_subgroup]
updated_owner_permissions =
owner_permissions - create_subgroup_permission
expect_disallowed(*create_subgroup_permission)
expect_allowed(*updated_owner_permissions)
end
end
context 'maintainer' do
let(:current_user) { maintainer }
it 'allows every maintainer permission except creating subgroups' do
create_subgroup_permission = [:create_subgroup]
updated_maintainer_permissions =
maintainer_permissions - create_subgroup_permission
expect_disallowed(*create_subgroup_permission)
expect_allowed(*updated_maintainer_permissions)
end
end
end
describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do
describe 'private nested group use the highest access level from the group and inherited permissions' do
let(:nested_group) do
create(:group, :private, :owner_subgroup_creation_only, parent: group)
end
......@@ -289,8 +236,6 @@ describe GroupPolicy do
let(:current_user) { owner }
it do
allow(Group).to receive(:supports_nested_objects?).and_return(true)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
......
......@@ -43,7 +43,7 @@ describe Clusters::ClusterPresenter do
end
shared_examples 'ancestor clusters' do
context 'ancestor clusters', :nested_groups do
context 'ancestor clusters' do
let(:root_group) { create(:group, name: 'Root Group') }
let(:parent) { create(:group, name: 'parent', parent: root_group) }
let(:child) { create(:group, name: 'child', parent: parent) }
......
......@@ -63,7 +63,7 @@ describe API::Boards do
end
end
describe "POST /groups/:id/boards/lists", :nested_groups do
describe "POST /groups/:id/boards/lists" do
set(:group) { create(:group) }
set(:board_parent) { create(:group, parent: group ) }
let(:url) { "/groups/#{board_parent.id}/boards/#{board.id}/lists" }
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe 'getting projects', :nested_groups do
describe 'getting projects' do
include GraphqlHelpers
let(:group) { create(:group) }
......
......@@ -94,7 +94,7 @@ describe API::GroupLabels do
expect(response).to have_gitlab_http_status(400)
end
it "does not delete parent's group labels", :nested_groups do
it "does not delete parent's group labels" do
subgroup = create(:group, parent: group)
subgroup_label = create(:group_label, title: 'feature', group: subgroup)
......@@ -127,7 +127,7 @@ describe API::GroupLabels do
expect(json_response['description']).to eq('test')
end
it "does not update parent's group label", :nested_groups do
it "does not update parent's group label" do
subgroup = create(:group, parent: group)
subgroup_label = create(:group_label, title: 'feature', group: subgroup)
......
......@@ -530,7 +530,7 @@ describe API::Groups do
expect(json_response.length).to eq(2)
end
it "returns projects including those in subgroups", :nested_groups do
it "returns projects including those in subgroups" do
subgroup = create(:group, parent: group1)
create(:project, group: subgroup)
create(:project, group: subgroup)
......@@ -642,7 +642,7 @@ describe API::Groups do
end
end
describe 'GET /groups/:id/subgroups', :nested_groups do
describe 'GET /groups/:id/subgroups' do
let!(:subgroup1) { create(:group, parent: group1) }
let!(:subgroup2) { create(:group, :private, parent: group1) }
let!(:subgroup3) { create(:group, :private, parent: group2) }
......@@ -786,7 +786,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(403)
end
context 'as owner', :nested_groups do
context 'as owner' do
before do
group2.add_owner(user1)
end
......@@ -798,7 +798,7 @@ describe API::Groups do
end
end
context 'as maintainer', :nested_groups do
context 'as maintainer' do
before do
group2.add_maintainer(user1)
end
......@@ -825,7 +825,7 @@ describe API::Groups do
expect(json_response["visibility"]).to eq(Gitlab::VisibilityLevel.string_level(Gitlab::CurrentSettings.current_application_settings.default_group_visibility))
end
it "creates a nested group", :nested_groups do
it "creates a nested group" do
parent = create(:group)
parent.add_owner(user3)
group = attributes_for(:group, { parent_id: parent.id })
......
......@@ -82,7 +82,7 @@ describe API::Issues do
end
end
context 'when group has subgroups', :nested_groups do
context 'when group has subgroups' do
let(:subgroup_1) { create(:group, parent: group) }
let(:subgroup_2) { create(:group, parent: subgroup_1) }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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