Commit 818caf0b5d1fc4f0cb2889ca5bd9e2d0d7fd8ac8

Authored by Sebastian Ziebell
1 parent 8045a81b

API: refined status code handling when adding or updating a project member

When a user is added to a project that is already a member of, a status code 201 is now returned to
signal an idempotent operation. If something fails then instead of returning error code 404 different
more specific error codes are returned. Status code 400 (Bad request) is returned when a required
attribute, e.g. `access_level` is not given or 422 if there is a semantic error, e.g. should
the `access_level` have an unsupported value.

Specs are added to check these status codes.
lib/api/projects.rb
... ... @@ -89,15 +89,26 @@ module Gitlab
89 89 # POST /projects/:id/members
90 90 post ":id/members" do
91 91 authorize! :admin_project, user_project
92   - users_project = user_project.users_projects.new(
93   - user_id: params[:user_id],
94   - project_access: params[:access_level]
95   - )
96 92  
97   - if users_project.save
98   - @member = users_project.user
  93 + error!("User id not given", 400) if !params.has_key? :user_id
  94 + error!("Access level not given", 400) if !params.has_key? :access_level
  95 +
  96 + # either the user is already a team member or a new one
  97 + team_member = user_project.team_member_by_id(params[:user_id])
  98 + if team_member.nil?
  99 + team_member = user_project.users_projects.new(
  100 + user_id: params[:user_id],
  101 + project_access: params[:access_level]
  102 + )
  103 + end
  104 +
  105 + if team_member.save
  106 + @member = team_member.user
99 107 present @member, with: Entities::ProjectMember, project: user_project
100 108 else
  109 + if team_member.errors[:project_access].any?
  110 + error!(team_member.errors[:project_access], 422)
  111 + end
101 112 not_found!
102 113 end
103 114 end
... ... @@ -112,12 +123,18 @@ module Gitlab
112 123 # PUT /projects/:id/members/:user_id
113 124 put ":id/members/:user_id" do
114 125 authorize! :admin_project, user_project
115   - users_project = user_project.users_projects.find_by_user_id params[:user_id]
116 126  
117   - if users_project.update_attributes(project_access: params[:access_level])
118   - @member = users_project.user
  127 + team_member = user_project.users_projects.find_by_user_id(params[:user_id])
  128 + error!("Access level not given", 400) if !params.has_key? :access_level
  129 + error!("User can not be found", 404) if team_member.nil?
  130 +
  131 + if team_member.update_attributes(project_access: params[:access_level])
  132 + @member = team_member.user
119 133 present @member, with: Entities::ProjectMember, project: user_project
120 134 else
  135 + if team_member.errors[:project_access].any?
  136 + error!(team_member.errors[:project_access], 422)
  137 + end
121 138 not_found!
122 139 end
123 140 end
... ...
spec/requests/api/projects_spec.rb
... ... @@ -177,6 +177,34 @@ describe Gitlab::API do
177 177 json_response['email'].should == user2.email
178 178 json_response['access_level'].should == UsersProject::DEVELOPER
179 179 end
  180 +
  181 + it "should return a 201 status if user is already project member" do
  182 + post api("/projects/#{project.id}/members", user), user_id: user2.id,
  183 + access_level: UsersProject::DEVELOPER
  184 + expect {
  185 + post api("/projects/#{project.id}/members", user), user_id: user2.id,
  186 + access_level: UsersProject::DEVELOPER
  187 + }.not_to change { UsersProject.count }.by(1)
  188 +
  189 + response.status.should == 201
  190 + json_response['email'].should == user2.email
  191 + json_response['access_level'].should == UsersProject::DEVELOPER
  192 + end
  193 +
  194 + it "should return a 400 error when user id is not given" do
  195 + post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER
  196 + response.status.should == 400
  197 + end
  198 +
  199 + it "should return a 400 error when access level is not given" do
  200 + post api("/projects/#{project.id}/members", user), user_id: user2.id
  201 + response.status.should == 400
  202 + end
  203 +
  204 + it "should return a 422 error when access level is not known" do
  205 + post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234
  206 + response.status.should == 422
  207 + end
180 208 end
181 209  
182 210 describe "PUT /projects/:id/members/:user_id" do
... ... @@ -186,6 +214,21 @@ describe Gitlab::API do
186 214 json_response['email'].should == user3.email
187 215 json_response['access_level'].should == UsersProject::MASTER
188 216 end
  217 +
  218 + it "should return a 404 error if user_id is not found" do
  219 + put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER
  220 + response.status.should == 404
  221 + end
  222 +
  223 + it "should return a 400 error when access level is not given" do
  224 + put api("/projects/#{project.id}/members/#{user3.id}", user)
  225 + response.status.should == 400
  226 + end
  227 +
  228 + it "should return a 422 error when access level is not known" do
  229 + put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123
  230 + response.status.should == 422
  231 + end
189 232 end
190 233  
191 234 describe "DELETE /projects/:id/members/:user_id" do
... ...