Unassign all assignees from merge request via Rest API not possible
Summary
The merge request API doesn't unassign all assignees if, as described in the API doc, setting assignee_id
to 0
or assignee_ids
to []
or [0]
.
Steps to reproduce
- Create a merge-request and assign any user
- Make an authorized API call as shown below:
## using `assignee_id` as parameter
~$ curl -X PUT -s -H "Authorization: Bearer $TOKEN" "https://my.gitlab.instance/api/v4/projects/:project_id/merge_requests/:iid" -d 'assignee_id=0'
## using `assignee_ids` as parameter
~$ curl -X PUT -s -H "Authorization: Bearer $TOKEN" "https://my.gitlab.instance/api/v4/projects/:project_id/merge_requests/:iid" -d 'assignee_ids=[]'
In both cases GitLab returns HTTP/2 200
but in the response body the assignees haven't changed.
Example Project
What is the current bug behavior?
- When unassigning user(s), nothing happens, the same users are assigned as before (no change)
- When re-assigning user(s), by using
assignee_id
orassignee_ids
to overwrite the current value, the assignee(s) of the merge-request are changed, but no log entry is shown in the "history".
What is the expected correct behavior?
All users get unassigned.
Relevant logs and/or screenshots
–
Output of checks
This bug happens on GitLab.com and on a self-hosted instance.
Results of GitLab environment info
cURL output of gitlab.com
for merge-request l0nax/changelog-go!25 (merged):
cURL output
### using field `assignee_id`
~$ curl -XPUT -s -H "Authorization: Bearer $TOKEN" "https://gitlab.com/api/v4/projects/13684181/merge_requests/25" -d 'assignee_id=0' -i
HTTP/2 200
date: Fri, 07 May 2021 16:12:49 GMT
content-type: application/json
vary: Accept-Encoding
cache-control: max-age=0, private, must-revalidate
vary: Origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-gitlab-feature-category: code_review
x-request-id: 01F53SMQJVH7V28VTN9PM07FFG
x-runtime: 0.216845
strict-transport-security: max-age=31536000
referrer-policy: strict-origin-when-cross-origin
ratelimit-observed: 1
ratelimit-remaining: 1999
ratelimit-reset: 1620404029
ratelimit-resettime: Fri, 07 May 2021 16:13:49 GMT
ratelimit-limit: 2000
gitlab-lb: fe-08-lb-gprd
gitlab-sv: localhost
cf-cache-status: DYNAMIC
cf-request-id: 09e93375cb0000ee2f492a9000000001
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 64bbbb694b9fee2f-CDG
{"id":99125007,"iid":25,"project_id":13684181,"title":"Deprecate 'gut' package","description":"The 'gut' package will be removed in the future since we will drop\n3rd-party VCS libraries/dependencies in favor of only getting the\nrelevant information via the stdlib.","state":"opened","created_at":"2021-05-07T16:10:37.867Z","updated_at":"2021-05-07T16:10:37.867Z","merged_by":null,"merged_at":null,"closed_by":null,"closed_at":null,"target_branch":"master","source_branch":"develop","user_notes_count":0,"upvotes":0,"downvotes":0,"author":{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"},"assignees":[{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"}],"assignee":{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"},"reviewers":[{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"}],"source_project_id":13684181,"target_project_id":13684181,"labels":[],"work_in_progress":false,"milestone":null,"merge_when_pipeline_succeeds":false,"merge_status":"can_be_merged","sha":"79f31783b722344b6c83a8311d13e4006d2e512e","merge_commit_sha":null,"squash_commit_sha":null,"discussion_locked":null,"should_remove_source_branch":null,"force_remove_source_branch":null,"reference":"!25","references":{"short":"!25","relative":"!25","full":"l0nax/changelog-go!25"},"web_url":"https://gitlab.com/l0nax/changelog-go/-/merge_requests/25","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"squash":false,"task_completion_status":{"count":0,"completed_count":0},"has_conflicts":false,"blocking_discussions_resolved":true,"approvals_before_merge":null,"subscribed":true,"changes_count":"15","latest_build_started_at":null,"latest_build_finished_at":null,"first_deployed_to_production_at":null,"pipeline":null,"head_pipeline":{"id":220684442,"project_id":13684181,"sha":"79f31783b722344b6c83a8311d13e4006d2e512e","ref":"develop","status":"success","created_at":"2020-11-24T17:25:10.196Z","updated_at":"2020-11-24T17:34:28.408Z","web_url":"https://gitlab.com/l0nax/changelog-go/-/pipelines/220684442","before_sha":"964e601bf492b0d8248af4e18b4125bc344f65d6","tag":false,"yaml_errors":null,"user":{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"},"started_at":"2020-11-24T17:25:18.512Z","finished_at":"2020-11-24T17:34:28.402Z","committed_at":null,"duration":545,"queued_duration":8,"coverage":null,"detailed_status":{"icon":"status_success","text":"passed","label":"passed","group":"success","tooltip":"passed","has_details":true,"details_path":"/l0nax/changelog-go/-/pipelines/220684442","illustration":null,"favicon":"https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"}},"diff_refs":{"base_sha":"2c121a3084cf166b31c3e9bd828578a921e25bfa","head_sha":"79f31783b722344b6c83a8311d13e4006d2e512e","start_sha":"2c121a3084cf166b31c3e9bd828578a921e25bfa"},"merge_error":null,"user":{"can_merge":true}}
### using field `assignee_ids`
~$ curl -XPUT -s -H "Authorization: Bearer $TOKEN" "https://gitlab.com/api/v4/projects/13684181/merge_requests/25" -d 'assignee_ids=[]' -i
HTTP/2 200
date: Fri, 07 May 2021 16:10:51 GMT
content-type: application/json
vary: Accept-Encoding
cache-control: max-age=0, private, must-revalidatevary: Origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-gitlab-feature-category: code_review
x-request-id: 01F53SH3YJ0BDF2QDX6B7NKW1S
x-runtime: 0.247570
strict-transport-security: max-age=31536000
referrer-policy: strict-origin-when-cross-origin
ratelimit-observed: 13
ratelimit-remaining: 1987
ratelimit-reset: 1620403911
ratelimit-resettime: Fri, 07 May 2021 16:11:51 GMT
ratelimit-limit: 2000
gitlab-lb: fe-09-lb-gprd
gitlab-sv: localhost
cf-cache-status: DYNAMIC
cf-request-id: 09e931a7620000ee1719374000000001
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 64bbb8856918ee17-CDG
{"id":99125007,"iid":25,"project_id":13684181,"title":"Deprecate 'gut' package","description":"The 'gut' package will be removed in the future since we will drop\n3rd-party VCS libraries/dependencies in favor of only getting the\nrelevant information via the stdlib.","state":"opened","created_at":"2021-05-07T16:10:37.867Z","updated_at":"2021-05-07T16:10:37.867Z","merged_by":null,"merged_at":null,"closed_by":null,"closed_at":null,"target_branch":"master","source_branch":"develop","user_notes_count":0,"upvotes":0,"downvotes":0,"author":{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"},"assignees":[{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"}],"assignee":{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"},"reviewers":[{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"}],"source_project_id":13684181,"target_project_id":13684181,"labels":[],"work_in_progress":false,"milestone":null,"merge_when_pipeline_succeeds":false,"merge_status":"checking","sha":"79f31783b722344b6c83a8311d13e4006d2e512e","merge_commit_sha":null,"squash_commit_sha":null,"discussion_locked":null,"should_remove_source_branch":null,"force_remove_source_branch":null,"reference":"!25","references":{"short":"!25","relative":"!25","full":"l0nax/changelog-go!25"},"web_url":"https://gitlab.com/l0nax/changelog-go/-/merge_requests/25","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"squash":false,"task_completion_status":{"count":0,"completed_count":0},"has_conflicts":false,"blocking_discussions_resolved":true,"approvals_before_merge":null,"subscribed":true,"changes_count":"15","latest_build_started_at":null,"latest_build_finished_at":null,"first_deployed_to_production_at":null,"pipeline":null,"head_pipeline":{"id":220684442,"project_id":13684181,"sha":"79f31783b722344b6c83a8311d13e4006d2e512e","ref":"develop","status":"success","created_at":"2020-11-24T17:25:10.196Z","updated_at":"2020-11-24T17:34:28.408Z","web_url":"https://gitlab.com/l0nax/changelog-go/-/pipelines/220684442","before_sha":"964e601bf492b0d8248af4e18b4125bc344f65d6","tag":false,"yaml_errors":null,"user":{"id":1565745,"name":"Emanuel Bennici","username":"l0nax","state":"active","avatar_url":"https://secure.gravatar.com/avatar/1281e0667faf8a5aab93d12f4a7e00d2?s=80\u0026d=identicon","web_url":"https://gitlab.com/l0nax"},"started_at":"2020-11-24T17:25:18.512Z","finished_at":"2020-11-24T17:34:28.402Z","committed_at":null,"duration":545,"queued_duration":8,"coverage":null,"detailed_status":{"icon":"status_success","text":"passed","label":"passed","group":"success","tooltip":"passed","has_details":true,"details_path":"/l0nax/changelog-go/-/pipelines/220684442","illustration":null,"favicon":"https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"}},"diff_refs":{"base_sha":"2c121a3084cf166b31c3e9bd828578a921e25bfa","head_sha":"79f31783b722344b6c83a8311d13e4006d2e512e","start_sha":"2c121a3084cf166b31c3e9bd828578a921e25bfa"},"merge_error":null,"user":{"can_merge":true}}
Results of GitLab application Check
Expand for output related to the GitLab application check
(For installations with omnibus-gitlab package run and paste the output of:
sudo gitlab-rake gitlab:check SANITIZE=true
)(For installations from source run and paste the output of:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true
)(we will only investigate if the tests are passing)
Possible fixes
I tried to trace the problem down, and it seems like, with my very limited knowledge of how GitLab internally works, that it has been introduced with !57523 (merged).
Method new_assignee_ids
in file app/services/merge_requests/update_assignees_service.rb removes all users from the assignee_ids
list/set if they do not have the read_merge_request
permission.
So new_assignee_ids
will return []
for []
or [0]
.
And the class/ worker which will be triggered can be found in app/workers/merge_requests/assignees_change_worker.rb.
perform
will return if the given users
parameter is blank (in line 21:
def perform(merge_request_id, user_id, old_assignee_ids)
merge_request = MergeRequest.find(merge_request_id)
current_user = User.find(user_id)
# if a user was added and then removed, or removed and then added
# while waiting for this job to run, assume that nothing happened.
users = User.id_in(old_assignee_ids - merge_request.assignee_ids)
return if users.blank?
The current assignee(s) will never be removed from the merge-request.
This line has been added in 95dd6407 by @alexkalderimis.