Migrate policy store update call from REST to GraphQl

What does this MR do and why?

Summary

This change migrates the policy update operation from a legacy REST API call to a GraphQL mutation, bringing it in line with how policy creation already works.

The updatePolicyStorePolicy REST call is replaced with a new governPolicyUpdate GraphQL mutation; the now-unused REST client method is removed in the stacked follow-up !255616 (merged). To avoid duplicating the list of policy fields across multiple queries, those fields are extracted into a reusable GraphQL fragment shared by both the fetch query and the new update mutation.

Error handling is simplified as a result: previously, the code had to inspect HTTP response bodies in multiple places to extract validation messages from different fields (message or error). Now that all writes go through GraphQL mutations, a rejected update always surfaces its reason through a single PolicyStoreMutationError, making the error-reading logic much shorter and consistent.

A small utility function (toMutationVariables) is introduced to translate the snake_case parameter names used internally into the camelCase names the GraphQL mutation expects, with a safety check that catches any unmapped parameters early rather than silently dropping them.

Tests are updated throughout to reflect the new error type and to remove references to the old REST mock setup.

References

Screenshots or screen recordings

Description UI
Update mutation instead of REST call

How to set up and validate locally

  1. Enable the policy store experiment for your organization in GDK, the same way you did for the create/delete migrations. The frontend is gated behind the same setting.
  2. Open /-/security/policy_store in the GDK, open an existing policy, click Edit, change the name, and Save. Expect the success alert on the detail page and the new name shown. In the browser's Network tab, expect a POST to /api/graphql with operationName governPolicyUpdate and no PATCH request.
  3. On the detail page, click Disable, then Enable. Expect the status badge and the version number to change without a page reload, and the GraphQL variables to contain only organizationId, policyId, and lifecycleState.
  4. Edit a policy without touching the Scope step and Save. Expect the request variables to not include policyScope.
  5. Rename a policy to a name that already exists in the organization and Save. Expect the inline "A policy with this name already exists" error on the name field, not a page alert.

Verification

  • jest ee/spec/frontend/policy_store ee/spec/frontend/api_spec.js → Test Suites: 31 passed, 31 total / Tests: 539 passed, 539 total
  • All six .graphql documents under policy_store/graphql/ validate against a fresh gitlab:graphql:schema:dump with the graphql package (local ESLint cannot see the Apollo schema dump).
  • Adversarial review (sp-reviewer): pass with findings. Fixed in this diff: unmapped params now throw instead of being dropped (F1), the update mapping spec pins the full shape with toEqual (F2), a restating comment was removed (F7). Kept for the record: dead getPolicyStorePolicy REST sibling (F3, pre-existing), apiErrorMessage naming (F4), and the toggle-after-delete behaviour change above (F5).

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

🤖 Generated with Claude Code

Edited by Artur Fedorov

Merge request reports

Loading
Loading