Migrate policy store single-policy read to GraphQL

What does this MR do and why?

Migrates the policy store single-policy read path from REST to GraphQL. fetchPolicy in ee/app/assets/javascripts/policy_store/policies.js (used by the detail page and by the editor's initial load) now calls the policies(ids: [id]) query on organization.policyStore and takes the first result, instead of hitting the REST endpoint.

The read depends on the policyRego field on the GovernPolicy GraphQL type — without it, the migration would silently drop the "Compiled policy Rego" card the detail page renders. That backend field was extracted to !252576 (merged), and this MR is stacked on it (target branch 617790-govern-policy-rego-graphql-field). Merge !252576 (merged) first, then retarget this MR to master.

fetchPolicy rejects when the filter returns no policy. Unknown or unauthorized ids come back as an empty/null list, never a GraphQL error, so this preserves the existing error UI contract.

Closes #617790

Notes:

  • ids are plain Int, not GlobalID. This follows an established team decision (!250240 (merged), continued in !251612 (merged)): policies are frozen Gitlab::PolicyStore::Policy gem value objects that can't build a GlobalID, and Int round-trips with the REST policy_id. This MR makes the detail page the first consumer bound by the 32-bit ceiling; widening the id field and arguments together is tracked in that decision, before the experiment graduates.
  • The imperative fetch stays in policies.js rather than moving to Vue Apollo smart queries, because the REST write paths still assign their responses to component state. The smart-query refactor is deliberately deferred to follow-up issue #624217, after the create/update/delete mutations migrate.
  • Api.getPolicyStorePolicy in ee/app/assets/javascripts/api.js is now caller-less. It's deliberately left in place — removing it and its api_spec block belongs to the e4–e6 mutation-migration cleanup.
  • The Apollo cache gets Organization.policyStore { merge: true } because the policies and catalogs queries now run concurrently and both write the non-normalizable policyStore object. The policy read uses fetchPolicy: 'network-only' since the still-REST writes never update the Apollo cache.

References

Screenshots or screen recordings

No user-visible changes. The detail and editor pages render the same data, now fetched over GraphQL instead of REST.

How to set up and validate locally

  1. Enable the experiment in the rails console: ApplicationSetting.current.update!(policy_store_experiment_enabled: true) (requires an EE license with security_orchestration_policies).
  2. As an organization owner, create a policy at /-/organizations/<org-path>/security/policy_store/new.
  3. Open the policy's detail page /-/organizations/<org-path>/security/policy_store/<id>. The page renders name, badges, trigger/rules/actions, scope and the compiled Rego cards. The network tab shows a POST /api/graphql getPolicyStorePolicies request and no GET /api/v4/organizations/:id/security/policy_store/:id.
  4. Open /-/organizations/<org-path>/security/policy_store/<id>/edit. The wizard loads prefilled through the same query.
  5. Visit a detail URL with a nonexistent id. You get the same "policy could not be loaded" alert as before.

Verification and review record

Local verification (worktree; CI is the final judge for graphql-verify and jest):

  • jest ee/spec/frontend/policy_store → Test Suites: 29 passed · Tests: 455 passed
  • rspec ee/spec/graphql/types/govern/policy_type_spec.rb → 3 examples, 0 failures
  • rspec ee/spec/requests/api/graphql/organizations/policy_store_policies_spec.rb ee/spec/graphql/resolvers/govern/policies_resolver_spec.rb → exit 0 (all green, incl. new policyRego request-level coverage)
  • Query document validated against the regenerated schema dump; prettier/eslint/rubocop clean on all changed files
  • GraphQL artifacts regenerated via rake gitlab:graphql:update_all in the same commit

Adversarial review (harness sp-reviewer) verdict: blocked — both blocking findings are resolved in this commit: a prettier violation in policies_spec.js (reformatted), and the query document being untracked at review time (committed). Review-driven hardening also included: network-only policy read, policyStore cache merge config, constant Sentry error message, mapping assertions for scope_rego/created_at/updated_at, killswitch (policyStore: null) spec case, request-level policyRego assertions. Declined with reasons: Number() guard on the id (identical UI outcome either way), extracting the shared rego derivation (needs a gem change), REST wrapper removal (e4–e6 cleanup).

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Edited by Artur Fedorov

Merge request reports

Loading
Loading