graphql: Add mutation to delete custom attributes from users

What does this MR do and why?

Implements a GraphQL mutation deleteUserCustomAttribute to delete custom attributes from users, providing feature parity with the existing REST API DELETE /users/:id/custom_attributes/:key endpoint.

This MR follows the patterns and learnings established in !217084 (merged) (project custom attributes delete mutation), incorporating feedback from the GitLab team review.

Key implementation details (aligned with project mutation):

  • Uses verb-noun naming pattern: DeleteUserCustomAttribute (consistent with DeleteProjectCustomAttribute)
  • Uses shared CustomAttributes::DestroyService for business logic
  • Uses shared Types::CustomAttributeType (not a project-specific type)
  • Uses :delete_custom_attribute permission (specific to delete action)
  • Uses GlobalIDType[::User] for user lookup
  • Request specs only (no unit specs, following established patterns)

Changes:

  • Add Mutations::Users::CustomAttributes::Delete mutation class
  • Add :delete_custom_attribute permission to UserPolicy for admins
  • Mount mutation in MutationType with milestone 18.9
  • Add policy spec for :delete_custom_attribute permission
  • Add request specs covering authorization, success, and error scenarios

Example mutation:

mutation {
  deleteUserCustomAttribute(input: {
    userId: "gid://gitlab/User/1"
    key: "department"
  }) {
    customAttribute {
      key
      value
    }
    errors
  }
}

🛠️ with ❤️ at Siemens

References

  • Part of #349396
  • Follows patterns from: !217084 (merged) (project custom attributes delete mutation)
  • REST API implementation: lib/api/custom_attributes_endpoints.rb:86-93
  • Shared service: app/services/custom_attributes/destroy_service.rb
  • Shared type: app/graphql/types/custom_attribute_type.rb

Screenshots or screen recordings

Not applicable - backend API changes only, no UI modifications.

How to set up and validate locally

  1. Ensure you have an admin user (or create one in Rails console with User.first.update!(admin: true))
  2. Create a custom attribute on a user via Rails console:
    user = User.first
    user.custom_attributes.create!(key: 'department', value: 'engineering')
  3. Navigate to GraphiQL explorer at /-/graphql-explorer
  4. Run the following mutation (replace with your user ID):
    mutation {
      deleteUserCustomAttribute(input: {
        userId: "gid://gitlab/User/1"
        key: "department"
      }) {
        customAttribute {
          key
          value
        }
        errors
      }
    }
  5. Verify the deleted attribute is returned and the attribute is removed from the user
  6. Try deleting a non-existent key and verify error is returned in the errors array

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

MR Checklist (@gerardo-navarro)
Edited by Gerardo Navarro

Merge request reports

Loading