Support custom attributes on groups

This commit is contained in:
Markus Koller 2017-09-18 17:07:38 +02:00 committed by Markus Koller
parent 6902848a9c
commit 1f773a8ef5
No known key found for this signature in database
GPG Key ID: A2B74A05A7A2B7B7
13 changed files with 105 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,6 @@
class GroupCustomAttribute < ActiveRecord::Base
belongs_to :group
validates :group, :key, :value, presence: true
validates :key, uniqueness: { scope: [:group_id] }
end

View File

@ -0,0 +1,5 @@
---
title: Support custom attributes on groups and projects
merge_request: 14593
author: Markus Koller
type: changed

View File

@ -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

View File

@ -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

View File

@ -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
```

View File

@ -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

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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) }

View File

@ -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