Return 400 when a release update sends an empty released_at
What does this MR do and why?
PUT /projects/:id/releases/:tag_name answers 500 Internal Server Error when released_at is an empty string or null, and the release keeps its date. Grape coerces "" to nil for the DateTime param, Releases::UpdateService passes released_at: nil to Release#update, and the NOT NULL constraint on releases.released_at raises ActiveRecord::NotNullViolation.
This MR declares the update param with allow_blank: false, so the API refuses both values with 400 Bad Request before the service runs. The GraphQL releaseUpdate mutation already refuses a null releasedAt. Creating a release is unchanged: there an absent released_at still defaults to the current time.
Only requests that answered 500 change, so this is a bug fix under the API style guide rather than a breaking change, and it needs no feature flag. The committed OpenAPI document does not change: its generator does not see allow_blank, so it still marks the parameter nullable.
References
Closes #637896
I found this while working on gitlab-mcp-server, probing the release update route on GitLab EE 19.5.0. I found no issue or MR about it.
Screenshots or screen recordings
No UI change. What the update answers:
| Body | Before | After |
|---|---|---|
{"released_at": ""} |
500 Internal Server Error |
400 {"error":"released_at is empty"} |
{"released_at": null} |
500 Internal Server Error |
400 {"error":"released_at is empty"} |
How to set up and validate locally
-
As a Developer of a project that has a release for the tag
v1.0, run:curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \ --header "Content-Type: application/json" --data '{"released_at": ""}' \ --url "http://gdk.test:3000/api/v4/projects/<id>/releases/v1.0" -
On master it answers
500andlog/exceptions_json.loghas theNotNullViolation; on this branch it answers400with{"error":"released_at is empty"}. -
Repeat with
{"released_at": null}: the same answers. The release keeps itsreleased_atevery time.
I sent both bodies to a GitLab EE 19.5.0 Docker instance as an administrator: each got 500, and exceptions_json.log has PG::NotNullViolation: ERROR: null value in column "released_at" of relation "releases" violates not-null constraint for both. The new examples in spec/requests/api/releases_spec.rb send the same bodies as JSON: on master's code both get 500, on this branch 400, and the release keeps its date.
Checks I ran (Ruby 3.3.11)
- With master's
lib/api/releases.rband this branch'sspec/requests/api/releases_spec.rb: 284 examples, 2 failures, 14 pending. The two failures are the new examples, each answered500with theNotNullViolation. - On this branch, that spec with
ee/spec/requests/api/releases_spec.rb,spec/services/releases/update_service_spec.rbandspec/requests/api/graphql/mutations/releases/update_spec.rb: 363 examples, 0 failures, 17 pending. The CE request spec again withFOSS_ONLY=1: 284 examples, 0 failures, 14 pending. bundle exec rake gitlab:openapi:v3:check_docs: OpenAPI v3 documentation is up to date.- RuboCop on the two files: no offenses. The commit message linter, and Danger's commit and title linters: no problems.
- Left to the pipeline: Ruby 3.3.12 and every job beyond these specs.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.