diff --git a/app/models/users_project.rb b/app/models/users_project.rb index 1b598483..3b951f0d 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -20,6 +20,23 @@ class UsersProject < ActiveRecord::Base delegate :name, :email, to: :user, prefix: true + def self.bulk_delete(project, user_ids) + UsersProject.transaction do + UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| + users_project.delete + end + end + end + + def self.bulk_update(project, user_ids, project_access) + UsersProject.transaction do + UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| + users_project.project_access = project_access + users_project.save + end + end + end + def self.bulk_import(project, user_ids, project_access) UsersProject.transaction do user_ids.each do |user_id| diff --git a/app/roles/team.rb b/app/roles/team.rb index 27b1cc65..8aef405a 100644 --- a/app/roles/team.rb +++ b/app/roles/team.rb @@ -36,4 +36,17 @@ module Team UsersProject.bulk_import(self, users_ids, access_role) self.update_repository end + + # Update multiple project users + # to same access role by user ids + def update_users_ids_to_role(users_ids, access_role) + UsersProject.bulk_update(self, users_ids, access_role) + self.update_repository + end + + # Delete multiple users from project by user ids + def delete_users_ids_from_team(users_ids) + UsersProject.bulk_delete(self, users_ids) + self.update_repository + end end diff --git a/doc/api/projects.md b/doc/api/projects.md index 5a20719f..72874e59 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -112,6 +112,66 @@ Parameters: Will return created project with status `201 Created` on success, or `404 Not found` on fail. +## Get project users + +Get users and access roles for existing project + +``` +GET /projects/:id/users +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project + +Will return users and their access roles with status `200 OK` on success, or `404 Not found` on fail. + +## Add project users + +Add users to exiting project + +``` +POST /projects/:id/users +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `user_ids` (required) - The ID list of users to add ++ `project_access` (required) - Project access level + +Will return status `201 Created` on success, or `404 Not found` on fail. + +## Update project users access level + +Update existing users to specified access level + +``` +PUT /projects/:id/users +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `user_ids` (required) - The ID list of users to add ++ `project_access` (required) - Project access level + +Will return status `200 OK` on success, or `404 Not found` on fail. + +## Delete project users + +Delete users from exiting project + +``` +DELETE /projects/:id/users +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `user_ids` (required) - The ID list of users to add + +Will return status `200 OK` on success, or `404 Not found` on fail. ## Project repository branches diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 96ccd87a..fef5328d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -16,6 +16,11 @@ module Gitlab expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at end + class UsersProject < Grape::Entity + expose :user, using: Entities::UserBasic + expose :project_access + end + class RepoObject < Grape::Entity expose :name, :commit end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index d45d1d82..7da83429 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -54,6 +54,55 @@ module Gitlab end end + # Get project users + # + # Parameters: + # id (required) - The ID or code name of a project + # Example Request: + # GET /projects/:id/users + get ":id/users" do + @users_projects = paginate user_project.users_projects + present @users_projects, with: Entities::UsersProject + end + + # Add users to project with specified access level + # + # Parameters: + # id (required) - The ID or code name of a project + # user_ids (required) - The ID list of users to add + # project_access (required) - Project access level + # Example Request: + # POST /projects/:id/users + post ":id/users" do + user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access]) + nil + end + + # Update users to specified access level + # + # Parameters: + # id (required) - The ID or code name of a project + # user_ids (required) - The ID list of users to add + # project_access (required) - New project access level to + # Example Request: + # PUT /projects/:id/add_users + put ":id/users" do + user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access]) + nil + end + + # Delete project users + # + # Parameters: + # id (required) - The ID or code name of a project + # user_ids (required) - The ID list of users to delete + # Example Request: + # DELETE /projects/:id/users + delete ":id/users" do + user_project.delete_users_ids_from_team(params[:user_ids].values) + nil + end + # Get a project repository branches # # Parameters: diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 1373748f..439aecce 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -4,8 +4,12 @@ describe Gitlab::API do include ApiHelpers let(:user) { Factory :user } + let(:user2) { Factory.create(:user) } + let(:user3) { Factory.create(:user) } let!(:project) { Factory :project, owner: user } let!(:snippet) { Factory :snippet, author: user, project: project, title: 'example' } + let!(:users_project) { Factory :users_project, user: user, project: project, project_access: UsersProject::MASTER } + let!(:users_project2) { Factory :users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER } before { project.add_access(user, :read) } describe "GET /projects" do @@ -104,6 +108,45 @@ describe Gitlab::API do end end + describe "GET /projects/:id/users" do + it "should return project users" do + get api("/projects/#{project.code}/users", user) + + response.status.should == 200 + + json_response.should be_an Array + json_response.count.should == 2 + json_response.first['user']['id'].should == user.id + end + end + + describe "POST /projects/:id/users" do + it "should add users to project" do + expect { + post api("/projects/#{project.code}/users", user), + user_ids: {"0" => user2.id}, project_access: UsersProject::DEVELOPER + }.to change {project.users_projects.where(:project_access => UsersProject::DEVELOPER).count}.by(1) + end + end + + describe "PUT /projects/:id/users" do + it "should update users to new access role" do + expect { + put api("/projects/#{project.code}/users", user), + user_ids: {"0" => user3.id}, project_access: UsersProject::MASTER + }.to change {project.users_projects.where(:project_access => UsersProject::MASTER).count}.by(1) + end + end + + describe "DELETE /projects/:id/users" do + it "should delete users from project" do + expect { + delete api("/projects/#{project.code}/users", user), + user_ids: {"0" => user3.id} + }.to change {project.users_projects.count}.by(-1) + end + end + describe "GET /projects/:id/repository/tags" do it "should return an array of project tags" do get api("/projects/#{project.code}/repository/tags", user)