Commit 01d4995a authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'remove-myself-from-project-api-7687' into 'master'

Allow a project member to leave the projected through the API

Closes #7687 


See merge request !3613
parents e933a0b0 6dbcb880
...@@ -37,6 +37,7 @@ v 8.7.0 (unreleased) ...@@ -37,6 +37,7 @@ v 8.7.0 (unreleased)
- ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591 - ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
- Update number of Todos in the sidebar when it's marked as "Done". !3600 - Update number of Todos in the sidebar when it's marked as "Done". !3600
- API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling) - API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
- API: User can leave a project through the API when not master or owner. !3613
v 8.6.5 v 8.6.5
- Fix importing from GitHub Enterprise. !3529 - Fix importing from GitHub Enterprise. !3529
......
...@@ -780,8 +780,10 @@ Parameters: ...@@ -780,8 +780,10 @@ Parameters:
- `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project - `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
- `user_id` (required) - The ID of a team member - `user_id` (required) - The ID of a team member
This method is idempotent and can be called multiple times with the same parameters. This method removes the project member if the user has the proper access rights to do so.
Revoking team membership for a user who is not currently a team member is considered success. It returns a status code 403 if the member does not have the proper rights to perform this action.
In all other cases this method is idempotent and revoking team membership for a user who is not
currently a team member is considered success.
Please note that the returned JSON currently differs slightly. Thus you should not Please note that the returned JSON currently differs slightly. Thus you should not
rely on the returned JSON structure. rely on the returned JSON structure.
......
...@@ -93,12 +93,17 @@ module API ...@@ -93,12 +93,17 @@ module API
# Example Request: # Example Request:
# DELETE /projects/:id/members/:user_id # DELETE /projects/:id/members/:user_id
delete ":id/members/:user_id" do delete ":id/members/:user_id" do
authorize! :admin_project, user_project
project_member = user_project.project_members.find_by(user_id: params[:user_id]) project_member = user_project.project_members.find_by(user_id: params[:user_id])
unless project_member.nil?
project_member.destroy unless current_user.can?(:admin_project, user_project) ||
else current_user.can?(:destroy_project_member, project_member)
forbidden!
end
if project_member.nil?
{ message: "Access revoked", id: params[:user_id].to_i } { message: "Access revoked", id: params[:user_id].to_i }
else
project_member.destroy
end end
end end
end end
......
...@@ -118,8 +118,10 @@ describe API::API, api: true do ...@@ -118,8 +118,10 @@ describe API::API, api: true do
end end
describe "DELETE /projects/:id/members/:user_id" do describe "DELETE /projects/:id/members/:user_id" do
before { project_member } before do
before { project_member2 } project_member
project_member2
end
it "should remove user from project team" do it "should remove user from project team" do
expect do expect do
...@@ -132,6 +134,7 @@ describe API::API, api: true do ...@@ -132,6 +134,7 @@ describe API::API, api: true do
expect do expect do
delete api("/projects/#{project.id}/members/#{user3.id}", user) delete api("/projects/#{project.id}/members/#{user3.id}", user)
end.to_not change { ProjectMember.count } end.to_not change { ProjectMember.count }
expect(response.status).to eq(200)
end end
it "should return 200 if team member already removed" do it "should return 200 if team member already removed" do
...@@ -145,8 +148,19 @@ describe API::API, api: true do ...@@ -145,8 +148,19 @@ describe API::API, api: true do
delete api("/projects/#{project.id}/members/1000000", user) delete api("/projects/#{project.id}/members/1000000", user)
end.to change { ProjectMember.count }.by(0) end.to change { ProjectMember.count }.by(0)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response['message']).to eq("Access revoked")
expect(json_response['id']).to eq(1000000) expect(json_response['id']).to eq(1000000)
expect(json_response['message']).to eq('Access revoked')
end
context 'when the user is not an admin or owner' do
it 'can leave the project' do
expect do
delete api("/projects/#{project.id}/members/#{user3.id}", user3)
end.to change { ProjectMember.count }.by(-1)
expect(response.status).to eq(200)
expect(json_response['id']).to eq(project_member2.id)
end
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment