Add OrganizationDelete GraphQL mutation and expose state fields
<!--IssueSummary start-->
<details>
<summary>
Everyone can contribute. [Help move this issue forward](https://handbook.gitlab.com/handbook/marketing/developer-relations/contributor-success/community-contributors-workflows/#contributor-links) while earning points, leveling up and collecting rewards.
</summary>
- [Collaborate/take over this issue](https://contributors.gitlab.com/manage-issue?action=work&projectId=278964&issueIid=594313)
</details>
<!--IssueSummary end-->
## What
- New mutation `OrganizationDelete` that calls `Organizations::MarkForDeletionService`
- Expose `state` and `deletion_scheduled_at` fields on `OrganizationType` (admin-only via `authorize: :admin_organization`)
- Mount the mutation in `MutationType`
```ruby
mount_mutation Mutations::Organizations::Delete
```
- Wrap behind a `delete_organization` feature flag for safe rollout
## Implementation plan
### New file: `app/graphql/mutations/organizations/delete.rb`
Authorizes via `:delete_organization`, accepts a Global ID, delegates to `Organizations::DeleteService`, returns the organization and errors.
```ruby
# frozen_string_literal: true
module Mutations
module Organizations
class Delete < Base
graphql_name 'OrganizationDelete'
authorize :delete_organization
argument :id,
Types::GlobalIDType[::Organizations::Organization],
required: true,
description: 'ID of the organization to soft-delete.'
field :organization,
Types::Organizations::OrganizationType,
null: true,
description: 'The soft-deleted organization.'
def resolve(id:)
organization = authorized_find!(id: id)
result = ::Organizations::MarkForDeletionService.new(
organization,
current_user: current_user
).execute
{ organization: result.payload[:organization], errors: result.errors }
end
end
end
end
```
### Changes to `app/graphql/types/organizations/organization_type.rb`
```ruby
field :state, GraphQL::Types::String, null: false,
description: 'State of the organization.',
authorize: :admin_organization
field :deletion_scheduled_at, Types::TimeType, null: true,
description: 'Timestamp when deletion was scheduled. Visible to admins only.',
authorize: :admin_organization
def deletion_scheduled_at
object.organization_detail.deletion_scheduled_at
end
```
### Tests
`spec/requests/api/graphql/mutations/organizations/delete_spec.rb`
- Succeeds for org owner on empty org (returns org in `deletion_scheduled` state with renamed name/path)
- Succeeds for instance admin on empty org
- Returns permission error for regular user
- Returns error when org has groups
- Returns error when org has projects
- Returns error for default org
- `state` field reflects `deletion_scheduled` after mutation
issue