Migrate policy store policy delete to GraphQL

What does this MR do and why?

Migrates the policy store detail page's policy delete from the REST endpoint to the governPolicyDelete GraphQL mutation, called directly from the detail component via this.$apollo.mutate (the detail app gains an apolloProvider for this). The store's rejection message is now surfaced in the delete alert instead of only a generic message. The deletePolicy wrapper is removed from policies.js along with the now-unused Api.deletePolicyStorePolicy REST method.

Closes #617793.

REST wrapper audit

  • Api.deletePolicyStorePolicy was the last consumer of the REST delete and is removed together with its spec; the deletePolicy wrapper in policies.js is removed entirely since the component now calls the mutation itself (verified by grep — no references remain).
  • fetchPolicies and updatePolicy still call REST and still have consumers (list page, status toggle) — they belong to the remaining migration issues.
  • The now-unused getPolicyStorePolicy REST method is left for the open fetch-migration MR !252321 (merged) to clean up.

Notes for reviewers

  • Policy store ids are plain Int at the GraphQL boundary, not GlobalIDs — the Number(policyId) cast follows the convention established in !250240 (merged) and continued in !251612 (merged) (ids are frozen gem value objects, not ActiveRecord models, so no GlobalID exists; REST takes the same plain integer).
  • The only payload error the backend can currently return for delete is the store's Policy was not found constant (already externalized as s_('GovernPolicies|Policy was not found') in base_service.rb). Surfacing it follows the acceptance criteria and matches how the create flow already surfaces store messages. Any other failure (experiment gate, unmapped reasons) arrives as a top-level GraphQL error and keeps the translated generic alert.
  • An adversarial review of the first revision (wrapper-based) returned "pass with findings" (verbatim verdict). The mutation call then moved from a policies.js wrapper into the component at the author's direction; error semantics, variables, and spec coverage carried over unchanged. Non-blocking findings kept as follow-ups: the payload destructure assumes a non-null payload (safe under Apollo's default errorPolicy: 'none', and consistent with createPolicy); Sentry still captures not-found deletes (pre-existing behavior); after a not-found the page still renders the deleted policy (pre-existing under REST).

References

Screenshots or screen recordings

No visual changes. The delete flow keeps the same confirmation modal and button; only the failure alert can now carry the store's message instead of always the generic copy.

How to set up and validate locally

  1. Enable the policy store experiment for an organization (GDK): in rails console, ensure organization.policy_store_experiment_active? returns true for your organization, and sign in as a user with the delete_govern_policy ability (instance admin / organization owner).
  2. Create a policy via the wizard at /-/organizations/<organization_path>/security/policy_store/new.
  3. Open the policy's detail page at /-/organizations/<organization_path>/security/policy_store/<id> and click Delete, then confirm.
  4. Expected: the network tab shows a governPolicyDelete GraphQL request (no DELETE /api/v4/organizations/:id/security/policy_store/:policy_id call), and the browser returns to the policy list.
  5. Delete the same policy again from a second tab opened beforehand: the alert shows the store's message (Policy was not found) instead of the generic copy.

Verification evidence:

  • local jest on the three changed spec files — Test Suites: 3 passed, Tests: 92 passed
  • eslint clean on all changed JS/Vue files
  • the new mutation document validated against tmp/tests/graphql/gitlab_schema.graphql with the graphql package (document valid)

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Edited by Artur Fedorov

Merge request reports

Loading
Loading