summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Koller <markus-koller@gmx.ch>2017-09-18 17:07:38 +0200
committerMarkus Koller <markus.koller.ext@siemens.com>2017-11-06 10:51:50 +0100
commit1f773a8ef5a1f76166d0455c6a5e473278885c17 (patch)
treeed2d955865b49aa5d102ccb4b4f64868d4d12b77
parent6902848a9c54f9eb1bfd82fe173ad0d5d62fe2d5 (diff)
downloadgitlab-ce-1f773a8ef5a1f76166d0455c6a5e473278885c17.tar.gz
Support custom attributes on groups
-rw-r--r--app/finders/groups_finder.rb8
-rw-r--r--app/models/group.rb1
-rw-r--r--app/models/group_custom_attribute.rb6
-rw-r--r--changelogs/unreleased/feature-custom-attributes-on-projects-and-groups.yml5
-rw-r--r--db/migrate/20170918140927_create_group_custom_attributes.rb19
-rw-r--r--db/schema.rb12
-rw-r--r--doc/api/custom_attributes.md9
-rw-r--r--doc/api/groups.md6
-rw-r--r--lib/api/groups.rb9
-rw-r--r--spec/factories/group_custom_attributes.rb7
-rw-r--r--spec/models/group_custom_attribute_spec.rb16
-rw-r--r--spec/models/group_spec.rb1
-rw-r--r--spec/requests/api/groups_spec.rb10
13 files changed, 105 insertions, 4 deletions
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 0c4c4b10fb6..0282b378d88 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -15,6 +15,8 @@
# Anonymous users will never return any `owned` groups. They will return all
# public groups instead, even if `all_available` is set to false.
class GroupsFinder < UnionFinder
+ include CustomAttributesFilter
+
def initialize(current_user = nil, params = {})
@current_user = current_user
@params = params
@@ -22,8 +24,12 @@ class GroupsFinder < UnionFinder
def execute
items = all_groups.map do |item|
- by_parent(item)
+ item = by_parent(item)
+ item = by_custom_attributes(item)
+
+ item
end
+
find_union(items, Group).with_route.order_id_desc
end
diff --git a/app/models/group.rb b/app/models/group.rb
index c660de7fcb6..8cf632fb566 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -26,6 +26,7 @@ class Group < Namespace
has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
has_many :labels, class_name: 'GroupLabel'
has_many :variables, class_name: 'Ci::GroupVariable'
+ has_many :custom_attributes, class_name: 'GroupCustomAttribute'
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :visibility_level_allowed_by_projects
diff --git a/app/models/group_custom_attribute.rb b/app/models/group_custom_attribute.rb
new file mode 100644
index 00000000000..8157d602d67
--- /dev/null
+++ b/app/models/group_custom_attribute.rb
@@ -0,0 +1,6 @@
+class GroupCustomAttribute < ActiveRecord::Base
+ belongs_to :group
+
+ validates :group, :key, :value, presence: true
+ validates :key, uniqueness: { scope: [:group_id] }
+end
diff --git a/changelogs/unreleased/feature-custom-attributes-on-projects-and-groups.yml b/changelogs/unreleased/feature-custom-attributes-on-projects-and-groups.yml
new file mode 100644
index 00000000000..9eae989a270
--- /dev/null
+++ b/changelogs/unreleased/feature-custom-attributes-on-projects-and-groups.yml
@@ -0,0 +1,5 @@
+---
+title: Support custom attributes on groups and projects
+merge_request: 14593
+author: Markus Koller
+type: changed
diff --git a/db/migrate/20170918140927_create_group_custom_attributes.rb b/db/migrate/20170918140927_create_group_custom_attributes.rb
new file mode 100644
index 00000000000..3879ea15eb6
--- /dev/null
+++ b/db/migrate/20170918140927_create_group_custom_attributes.rb
@@ -0,0 +1,19 @@
+class CreateGroupCustomAttributes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :group_custom_attributes do |t|
+ t.timestamps_with_timezone null: false
+ t.references :group, null: false
+ t.string :key, null: false
+ t.string :value, null: false
+
+ t.index [:group_id, :key], unique: true
+ t.index [:key, :value]
+ end
+
+ add_foreign_key :group_custom_attributes, :namespaces, column: :group_id, on_delete: :cascade # rubocop: disable Migration/AddConcurrentForeignKey
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 76fc92e2282..54127087b8b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -693,6 +693,17 @@ ActiveRecord::Schema.define(version: 20171026082505) do
add_index "gpg_signatures", ["gpg_key_subkey_id"], name: "index_gpg_signatures_on_gpg_key_subkey_id", using: :btree
add_index "gpg_signatures", ["project_id"], name: "index_gpg_signatures_on_project_id", using: :btree
+ create_table "group_custom_attributes", force: :cascade do |t|
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "group_id", null: false
+ t.string "key", null: false
+ t.string "value", null: false
+ end
+
+ add_index "group_custom_attributes", ["group_id", "key"], name: "index_group_custom_attributes_on_group_id_and_key", unique: true, using: :btree
+ add_index "group_custom_attributes", ["key", "value"], name: "index_group_custom_attributes_on_key_and_value", using: :btree
+
create_table "identities", force: :cascade do |t|
t.string "extern_uid"
t.string "provider"
@@ -1840,6 +1851,7 @@ ActiveRecord::Schema.define(version: 20171026082505) do
add_foreign_key "gpg_signatures", "gpg_key_subkeys", on_delete: :nullify
add_foreign_key "gpg_signatures", "gpg_keys", on_delete: :nullify
add_foreign_key "gpg_signatures", "projects", on_delete: :cascade
+ add_foreign_key "group_custom_attributes", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade
add_foreign_key "issue_assignees", "users", name: "fk_5e0c8d9154", on_delete: :cascade
add_foreign_key "issue_metrics", "issues", on_delete: :cascade
diff --git a/doc/api/custom_attributes.md b/doc/api/custom_attributes.md
index 705f1d401cf..91d1b0e1520 100644
--- a/doc/api/custom_attributes.md
+++ b/doc/api/custom_attributes.md
@@ -1,8 +1,9 @@
# Custom Attributes API
Every API call to custom attributes must be authenticated as administrator.
-Custom attributes are currently available on users and projects, which will
-be referred to as "resource" in this documentation.
+
+Custom attributes are currently available on users, groups, and projects,
+which will be referred to as "resource" in this documentation.
## List custom attributes
@@ -10,6 +11,7 @@ Get all custom attributes on a resource.
```
GET /users/:id/custom_attributes
+GET /groups/:id/custom_attributes
GET /projects/:id/custom_attributes
```
@@ -42,6 +44,7 @@ Get a single custom attribute on a resource.
```
GET /users/:id/custom_attributes/:key
+GET /groups/:id/custom_attributes/:key
GET /projects/:id/custom_attributes/:key
```
@@ -70,6 +73,7 @@ or newly created otherwise.
```
PUT /users/:id/custom_attributes/:key
+PUT /groups/:id/custom_attributes/:key
PUT /projects/:id/custom_attributes/:key
```
@@ -98,6 +102,7 @@ Delete a custom attribute on a resource.
```
DELETE /users/:id/custom_attributes/:key
+DELETE /groups/:id/custom_attributes/:key
DELETE /projects/:id/custom_attributes/:key
```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 99d200c9c93..16db9c2f259 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -74,6 +74,12 @@ GET /groups?statistics=true
You can search for groups by name or path, see below.
+You can filter by [custom attributes](custom_attributes.md) with:
+
+```
+GET /groups?custom_attributes[key]=value&custom_attributes[other_key]=other_value
+```
+
## List a group's projects
Get a list of projects in this group. When accessed without authentication, only
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index e817dcbbc4b..340a7cecf09 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -37,6 +37,8 @@ module API
end
resource :groups do
+ include CustomAttributesEndpoints
+
desc 'Get a groups list' do
success Entities::Group
end
@@ -51,7 +53,12 @@ module API
use :pagination
end
get do
- find_params = { all_available: params[:all_available], owned: params[:owned] }
+ find_params = {
+ all_available: params[:all_available],
+ owned: params[:owned],
+ custom_attributes: params[:custom_attributes]
+ }
+
groups = GroupsFinder.new(current_user, find_params).execute
groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
diff --git a/spec/factories/group_custom_attributes.rb b/spec/factories/group_custom_attributes.rb
new file mode 100644
index 00000000000..7ff5f376e8b
--- /dev/null
+++ b/spec/factories/group_custom_attributes.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+ factory :group_custom_attribute do
+ group
+ sequence(:key) { |n| "key#{n}" }
+ sequence(:value) { |n| "value#{n}" }
+ end
+end
diff --git a/spec/models/group_custom_attribute_spec.rb b/spec/models/group_custom_attribute_spec.rb
new file mode 100644
index 00000000000..7ecb2022567
--- /dev/null
+++ b/spec/models/group_custom_attribute_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe GroupCustomAttribute do
+ describe 'assocations' do
+ it { is_expected.to belong_to(:group) }
+ end
+
+ describe 'validations' do
+ subject { build :group_custom_attribute }
+
+ it { is_expected.to validate_presence_of(:group) }
+ it { is_expected.to validate_presence_of(:key) }
+ it { is_expected.to validate_presence_of(:value) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id) }
+ end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 0e1a7fdce0b..c8caa11b8b0 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -17,6 +17,7 @@ describe Group do
it { is_expected.to have_many(:variables).class_name('Ci::GroupVariable') }
it { is_expected.to have_many(:uploads).dependent(:destroy) }
it { is_expected.to have_one(:chat_team) }
+ it { is_expected.to have_many(:custom_attributes).class_name('GroupCustomAttribute') }
describe '#members & #requesters' do
let(:requester) { create(:user) }
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 8ce9fcc80bf..780dbce6488 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -618,4 +618,14 @@ describe API::Groups do
end
end
end
+
+ it_behaves_like 'custom attributes endpoints', 'groups' do
+ let(:attributable) { group1 }
+ let(:other_attributable) { group2 }
+ let(:user) { user1 }
+
+ before do
+ group2.add_owner(user1)
+ end
+ end
end