Add REST API endpoint to restore an organization

What does this MR do and why?

Closes #599346

Administrators can now restore a soft-deleted organization with the REST API, using POST /api/v4/organizations/:id/restore. Until now, only the GraphQL organizationRestore mutation could do this. The endpoint follows the same rules as the mutation:

  • 201 with the restored organization when it works.
  • 400 with Organization is not soft-deleted when the organization is active.
  • 403 for users who aren't administrators, and for administrators without admin mode.
  • 404 when the organization doesn't exist.

Some decisions, and I'm happy to change any of them:

  1. When someone creates a fine-grained access token, only administrators are offered the restore permission. The mutation does the same, and the permissions check requires both to match.
  2. It returns 201 Created, like POST /api/v4/groups/:id/restore.
  3. No feature flag. The endpoint is marked as an experiment, like DELETE /api/v4/organizations/:id, which has no flag either.
  4. The endpoint description says GitLab 19.6, because I don't expect this to merge before the 19.5 release.

Three files are normally generated: the OpenAPI v3 docs, the fine-grained token permissions page, and the Cells route list. I wrote the entries by hand, and the jobs that check them passed (static-analysis, permissions-verify, cells-routes:up-to-date).

I added request tests for the restore itself, for each error response above, for 401 without a user, and for fine-grained tokens. They ran in pipeline 2933495983: rspec integration predictive passed with 46 examples, 0 failures and 6 pending. rubocop passed too. Locally I ran ruby -c on the changed Ruby files, and parsed the OpenAPI file as YAML. The rest runs in the pipeline.

The issue asks for docs on the organizations API page. I left them out, like for the delete endpoint, because !257880 (merged) removed the soft-delete docs while organizations are in beta. We agreed on this in the review.

I have one question:

  1. Danger says the HTTP Router route snapshot doesn't have the new route yet, and cells-routes:router-in-sync failed (it's allowed to fail). The same warning lists 11 other routes that aren't from this MR. Do I need to open the paired snapshot MR in the HTTP Router project, or does the Cells team refresh the snapshot?

References

Screenshots or screen recordings

Not applicable. This is a new API endpoint, with no UI change.

How to set up and validate locally

  1. Create a personal access token for an administrator with the api scope (and admin_mode if admin mode is enabled).
  2. In the Rails console, soft-delete an organization:
    organization = Organizations::Organization.find_by_path('my-org')
    organization.soft_delete(transition_user: User.find_by_username('root'))
    organization.reload.state # => "soft_deleted"
  3. Restore it with the API:
    curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
      "http://gdk.test:3000/api/v4/organizations/<organization_id>/restore"
  4. The response should be 201 with the organization, and organization.reload.state should be "active".
  5. Run the same curl again. It should return 400 with {"message":"Organization is not soft-deleted"}.

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.

Implemented with AI assistance; I reviewed and tested the changes.

Edited by Hamza Hassanain

Merge request reports

Loading
Loading