Skip to content
Snippets Groups Projects
Unverified Commit 6902848a authored by Markus Koller's avatar Markus Koller Committed by Markus Koller
Browse files

Support custom attributes on projects

parent 823a9d35
No related branches found
No related tags found
No related merge requests found
Showing
with 131 additions and 11 deletions
Loading
Loading
@@ -18,6 +18,8 @@
# non_archived: boolean
#
class ProjectsFinder < UnionFinder
include CustomAttributesFilter
attr_accessor :params
attr_reader :current_user, :project_ids_relation
 
Loading
Loading
@@ -44,6 +46,7 @@ class ProjectsFinder < UnionFinder
collection = by_tags(collection)
collection = by_search(collection)
collection = by_archived(collection)
collection = by_custom_attributes(collection)
 
sort(collection)
end
Loading
Loading
Loading
Loading
@@ -213,6 +213,7 @@ class Project < ActiveRecord::Base
has_many :active_runners, -> { active }, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
 
has_one :auto_devops, class_name: 'ProjectAutoDevops'
has_many :custom_attributes, class_name: 'ProjectCustomAttribute'
 
accepts_nested_attributes_for :variables, allow_destroy: true
accepts_nested_attributes_for :project_feature, update_only: true
Loading
Loading
class ProjectCustomAttribute < ActiveRecord::Base
belongs_to :project
validates :project, :key, :value, presence: true
validates :key, uniqueness: { scope: [:project_id] }
end
class CreateProjectCustomAttributes < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :project_custom_attributes do |t|
t.timestamps_with_timezone null: false
t.references :project, null: false, foreign_key: { on_delete: :cascade }
t.string :key, null: false
t.string :value, null: false
t.index [:project_id, :key], unique: true
t.index [:key, :value]
end
end
end
Loading
Loading
@@ -1214,6 +1214,17 @@ ActiveRecord::Schema.define(version: 20171026082505) do
 
add_index "project_auto_devops", ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree
 
create_table "project_custom_attributes", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "project_id", null: false
t.string "key", null: false
t.string "value", null: false
end
add_index "project_custom_attributes", ["key", "value"], name: "index_project_custom_attributes_on_key_and_value", using: :btree
add_index "project_custom_attributes", ["project_id", "key"], name: "index_project_custom_attributes_on_project_id_and_key", unique: true, using: :btree
create_table "project_features", force: :cascade do |t|
t.integer "project_id"
t.integer "merge_requests_access_level"
Loading
Loading
@@ -1859,6 +1870,7 @@ ActiveRecord::Schema.define(version: 20171026082505) do
add_foreign_key "project_authorizations", "projects", on_delete: :cascade
add_foreign_key "project_authorizations", "users", on_delete: :cascade
add_foreign_key "project_auto_devops", "projects", on_delete: :cascade
add_foreign_key "project_custom_attributes", "projects", on_delete: :cascade
add_foreign_key "project_features", "projects", name: "fk_18513d9b92", on_delete: :cascade
add_foreign_key "project_group_links", "projects", name: "fk_daa8cee94c", on_delete: :cascade
add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade
Loading
Loading
# 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.
 
## List custom attributes
 
Get all custom attributes on a user.
Get all custom attributes on a resource.
 
```
GET /users/:id/custom_attributes
GET /projects/:id/custom_attributes
```
 
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a user |
| `id` | integer | yes | The ID of a resource |
 
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/42/custom_attributes
Loading
Loading
@@ -35,15 +38,16 @@ Example response:
 
## Single custom attribute
 
Get a single custom attribute on a user.
Get a single custom attribute on a resource.
 
```
GET /users/:id/custom_attributes/:key
GET /projects/:id/custom_attributes/:key
```
 
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a user |
| `id` | integer | yes | The ID of a resource |
| `key` | string | yes | The key of the custom attribute |
 
```bash
Loading
Loading
@@ -61,16 +65,17 @@ Example response:
 
## Set custom attribute
 
Set a custom attribute on a user. The attribute will be updated if it already exists,
Set a custom attribute on a resource. The attribute will be updated if it already exists,
or newly created otherwise.
 
```
PUT /users/:id/custom_attributes/:key
PUT /projects/:id/custom_attributes/:key
```
 
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a user |
| `id` | integer | yes | The ID of a resource |
| `key` | string | yes | The key of the custom attribute |
| `value` | string | yes | The value of the custom attribute |
 
Loading
Loading
@@ -89,15 +94,16 @@ Example response:
 
## Delete custom attribute
 
Delete a custom attribute on a user.
Delete a custom attribute on a resource.
 
```
DELETE /users/:id/custom_attributes/:key
DELETE /projects/:id/custom_attributes/:key
```
 
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a user |
| `id` | integer | yes | The ID of a resource |
| `key` | string | yes | The key of the custom attribute |
 
```bash
Loading
Loading
Loading
Loading
@@ -192,6 +192,12 @@ GET /projects
]
```
 
You can filter by [custom attributes](custom_attributes.md) with:
```
GET /projects?custom_attributes[key]=value&custom_attributes[other_key]=other_value
```
## List user projects
 
Get a list of visible projects for the given user. When accessed without
Loading
Loading
Loading
Loading
@@ -328,6 +328,7 @@ module API
finder_params[:archived] = params[:archived]
finder_params[:search] = params[:search] if params[:search]
finder_params[:user] = params.delete(:user) if params[:user]
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
finder_params
end
 
Loading
Loading
Loading
Loading
@@ -119,6 +119,8 @@ module API
end
 
resource :projects do
include CustomAttributesEndpoints
desc 'Get a list of visible projects for authenticated user' do
success Entities::BasicProjectDetails
end
Loading
Loading
Loading
Loading
@@ -63,6 +63,7 @@ project_tree:
- protected_tags:
- :create_access_levels
- :project_feature
- :custom_attributes
 
# Only include the following attributes for the models specified.
included_attributes:
Loading
Loading
Loading
Loading
@@ -17,7 +17,8 @@ module Gitlab
labels: :project_labels,
priorities: :label_priorities,
auto_devops: :project_auto_devops,
label: :project_label }.freeze
label: :project_label,
custom_attributes: 'ProjectCustomAttribute' }.freeze
 
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id last_edited_by_id merge_user_id resolved_by_id].freeze
 
Loading
Loading
FactoryGirl.define do
factory :project_custom_attribute do
project
sequence(:key) { |n| "key#{n}" }
sequence(:value) { |n| "value#{n}" }
end
end
Loading
Loading
@@ -276,6 +276,7 @@ project:
- root_of_fork_network
- fork_network_member
- fork_network
- custom_attributes
award_emoji:
- awardable
- user
Loading
Loading
Loading
Loading
@@ -7408,5 +7408,23 @@
"snippets_access_level": 20,
"updated_at": "2016-09-23T11:58:28.000Z",
"wiki_access_level": 20
}
},
"custom_attributes": [
{
"id": 1,
"created_at": "2017-10-19T15:36:23.466Z",
"updated_at": "2017-10-19T15:36:23.466Z",
"project_id": 5,
"key": "foo",
"value": "foo"
},
{
"id": 2,
"created_at": "2017-10-19T15:37:21.904Z",
"updated_at": "2017-10-19T15:37:21.904Z",
"project_id": 5,
"key": "bar",
"value": "bar"
}
]
}
Loading
Loading
@@ -133,6 +133,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(@project.project_feature).not_to be_nil
end
 
it 'has custom attributes' do
expect(@project.custom_attributes.count).to eq(2)
end
it 'restores the correct service' do
expect(CustomIssueTrackerService.first).not_to be_nil
end
Loading
Loading
Loading
Loading
@@ -168,6 +168,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(project_feature["builds_access_level"]).to eq(ProjectFeature::PRIVATE)
end
 
it 'has custom attributes' do
expect(saved_project_json['custom_attributes'].count).to eq(2)
end
it 'does not complain about non UTF-8 characters in MR diffs' do
ActiveRecord::Base.connection.execute("UPDATE merge_request_diffs SET st_diffs = '---\n- :diff: !binary |-\n LS0tIC9kZXYvbnVsbAorKysgYi9pbWFnZXMvbnVjb3IucGRmCkBAIC0wLDAg\n KzEsMTY3OSBAQAorJVBERi0xLjUNJeLjz9MNCisxIDAgb2JqDTw8L01ldGFk\n YXR'")
 
Loading
Loading
@@ -279,6 +283,9 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
create(:event, :created, target: milestone, project: project, author: user)
create(:service, project: project, type: 'CustomIssueTrackerService', category: 'issue_tracker')
 
create(:project_custom_attribute, project: project)
create(:project_custom_attribute, project: project)
project
end
 
Loading
Loading
Loading
Loading
@@ -508,4 +508,11 @@ ProjectAutoDevops:
- updated_at
IssueAssignee:
- user_id
- issue_id
\ No newline at end of file
- issue_id
ProjectCustomAttribute:
- id
- created_at
- updated_at
- project_id
- key
- value
require 'spec_helper'
describe ProjectCustomAttribute do
describe 'assocations' do
it { is_expected.to belong_to(:project) }
end
describe 'validations' do
subject { build :project_custom_attribute }
it { is_expected.to validate_presence_of(:project) }
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(:project_id) }
end
end
Loading
Loading
@@ -79,6 +79,7 @@ describe Project do
it { is_expected.to have_many(:pipeline_schedules) }
it { is_expected.to have_many(:members_and_requesters) }
it { is_expected.to have_one(:cluster) }
it { is_expected.to have_many(:custom_attributes).class_name('ProjectCustomAttribute') }
 
context 'after initialized' do
it "has a project_feature" do
Loading
Loading
Loading
Loading
@@ -1856,4 +1856,9 @@ describe API::Projects do
end
end
end
it_behaves_like 'custom attributes endpoints', 'projects' do
let(:attributable) { project }
let(:other_attributable) { project2 }
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment