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:
idsare plainInt, not GlobalID. This follows an established team decision (!250240 (merged), continued in !251612 (merged)): policies are frozenGitlab::PolicyStore::Policygem value objects that can't build a GlobalID, andIntround-trips with the RESTpolicy_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.jsrather 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.getPolicyStorePolicyinee/app/assets/javascripts/api.jsis now caller-less. It's deliberately left in place — removing it and itsapi_specblock 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-normalizablepolicyStoreobject. The policy read usesfetchPolicy: 'network-only'since the still-REST writes never update the Apollo cache.
References
- Backend
policyRegofield (this MR's base): !252576 (merged) - Approved implementation plan: https://gitlab.com/gitlab-org/gitlab/-/work_items/617790#note_3747189040
- Epic: https://gitlab.com/groups/gitlab-org/-/epics/22542
- Backend
idsargument: !251612 (merged) - Int-ids decision: !250240 (merged)
- Follow-up smart-query refactor: #624217
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
- Enable the experiment in the rails console:
ApplicationSetting.current.update!(policy_store_experiment_enabled: true)(requires an EE license withsecurity_orchestration_policies). - As an organization owner, create a policy at
/-/organizations/<org-path>/security/policy_store/new. - 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 aPOST /api/graphqlgetPolicyStorePoliciesrequest and noGET /api/v4/organizations/:id/security/policy_store/:id. - Open
/-/organizations/<org-path>/security/policy_store/<id>/edit. The wizard loads prefilled through the same query. - 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 passedrspec ee/spec/graphql/types/govern/policy_type_spec.rb→3 examples, 0 failuresrspec 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. newpolicyRegorequest-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_allin 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.