diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index a50bc4a9057..bc88dc2d092 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -59,11 +59,11 @@
} else {
avatar = gon.default_avatar_url;
}
- return "
" + group.name + "
" + group.path + "
";
+ return " " + group.full_name + "
" + group.full_path + "
";
};
GroupsSelect.prototype.formatSelection = function(group) {
- return group.name;
+ return group.full_name;
};
return GroupsSelect;
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index 489e567259c..b1c0dc37b4d 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -13,12 +13,12 @@
filterable: true,
fieldName: 'group_id',
search: {
- fields: ['name']
+ fields: ['full_name']
},
data: function(term, callback) {
return Api.groups(term, {}, function(data) {
data.unshift({
- name: 'Any'
+ full_name: 'Any'
});
data.splice(1, 0, 'divider');
return callback(data);
@@ -28,10 +28,10 @@
return obj.id;
},
text: function(obj) {
- return obj.name;
+ return obj.full_name;
},
toggleLabel: function(obj) {
- return ($groupDropdown.data('default-label')) + " " + obj.name;
+ return ($groupDropdown.data('default-label')) + " " + obj.full_name;
},
clicked: (function(_this) {
return function() {
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 6654f6997ce..37b69423c97 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -89,7 +89,7 @@ module SearchHelper
{
category: "Groups",
id: group.id,
- label: "#{search_result_sanitize(group.name)}",
+ label: "#{search_result_sanitize(group.full_name)}",
url: group_path(group)
}
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 4396ea9ef36..37f4705adbd 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -225,6 +225,7 @@ class Project < ActiveRecord::Base
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
scope :with_statistics, -> { includes(:statistics) }
scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
+ scope :inside_path, ->(path) { joins(:route).where('routes.path LIKE ?', "#{path}/%") }
# "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) {
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index aa9837038a6..781cd13b44b 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -9,7 +9,10 @@ module Search
def execute
group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
projects = ProjectsFinder.new.execute(current_user)
- projects = projects.in_namespace(group.id) if group
+
+ if group
+ projects = projects.inside_path(group.full_path)
+ end
Gitlab::SearchResults.new(current_user, projects, params[:search])
end
diff --git a/changelogs/unreleased/dz-nested-groups-improvements-2.yml b/changelogs/unreleased/dz-nested-groups-improvements-2.yml
new file mode 100644
index 00000000000..8e4eb7f1fff
--- /dev/null
+++ b/changelogs/unreleased/dz-nested-groups-improvements-2.yml
@@ -0,0 +1,4 @@
+---
+title: Add read-only full_path and full_name attributes to Group API
+merge_request: 8827
+author:
diff --git a/doc/api/groups.md b/doc/api/groups.md
index f7807390e68..3b38e3e1bee 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -25,7 +25,14 @@ GET /groups
"id": 1,
"name": "Foobar Group",
"path": "foo-bar",
- "description": "An interesting group"
+ "description": "An interesting group",
+ "visibility_level": 20,
+ "lfs_enabled": true,
+ "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",
+ "web_url": "http://localhost:3000/groups/foo-bar",
+ "request_access_enabled": false,
+ "full_name": "Foobar Group",
+ "full_path": "foo-bar"
}
]
```
@@ -149,6 +156,8 @@ Example response:
"avatar_url": null,
"web_url": "https://gitlab.example.com/groups/twitter",
"request_access_enabled": false,
+ "full_name": "Foobar Group",
+ "full_path": "foo-bar",
"projects": [
{
"id": 7,
@@ -372,6 +381,8 @@ Example response:
"avatar_url": null,
"web_url": "http://gitlab.example.com/groups/h5bp",
"request_access_enabled": false,
+ "full_name": "Foobar Group",
+ "full_path": "foo-bar",
"projects": [
{
"id": 9,
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9f59939e9ae..a07b2a9ca0f 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -137,6 +137,7 @@ module API
expose :avatar_url
expose :web_url
expose :request_access_enabled
+ expose :full_name, :full_path
expose :statistics, if: :statistics do
with_options format_with: -> (value) { value.to_i } do
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index e51720f10ed..b7e547dc1f5 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -41,6 +41,11 @@ describe SearchHelper do
expect(search_autocomplete_opts("gro").size).to eq(1)
end
+ it "includes nested group" do
+ create(:group, :nested, name: 'foo').add_owner(user)
+ expect(search_autocomplete_opts('foo').size).to eq(1)
+ end
+
it "includes the user's projects" do
project = create(:empty_project, namespace: create(:namespace, owner: user))
expect(search_autocomplete_opts(project.name).size).to eq(1)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 527eb704036..48b085781e7 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1833,6 +1833,14 @@ describe Project, models: true do
end
end
+ describe 'inside_path' do
+ let!(:project1) { create(:empty_project) }
+ let!(:project2) { create(:empty_project) }
+ let!(:path) { project1.namespace.path }
+
+ it { expect(Project.inside_path(path)).to eq([project1]) }
+ end
+
def enable_lfs
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index edbf0140583..1187d2e609d 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -176,6 +176,9 @@ describe API::Groups, api: true do
expect(json_response['visibility_level']).to eq(group1.visibility_level)
expect(json_response['avatar_url']).to eq(group1.avatar_url)
expect(json_response['web_url']).to eq(group1.web_url)
+ expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
+ expect(json_response['full_name']).to eq(group1.full_name)
+ expect(json_response['full_path']).to eq(group1.full_path)
expect(json_response['projects']).to be_an Array
expect(json_response['projects'].length).to eq(2)
expect(json_response['shared_projects']).to be_an Array
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index bd89c4a7c11..bed1031e40a 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -41,6 +41,25 @@ describe 'Search::GlobalService', services: true do
results = context.execute
expect(results.objects('projects')).to match_array [found_project]
end
+
+ context 'nested group' do
+ let!(:nested_group) { create(:group, :nested) }
+ let!(:project) { create(:project, namespace: nested_group) }
+
+ before { project.add_master(user) }
+
+ it 'returns result from nested group' do
+ context = Search::GlobalService.new(user, search: project.path)
+ results = context.execute
+ expect(results.objects('projects')).to match_array [project]
+ end
+
+ it 'returns result from descendants when search inside group' do
+ context = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent)
+ results = context.execute
+ expect(results.objects('projects')).to match_array [project]
+ end
+ end
end
end
end