Skip to content

Commit 1cd3bb9

Browse files
nl0claude
andauthored
Catalog: cascade bucketRemove through cached Policy/Role permissions (#4855)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 96406df commit 1cd3bb9

2 files changed

Lines changed: 28 additions & 0 deletions

File tree

catalog/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ where verb is one of
1818

1919
## Changes
2020

21+
- [Fixed] Admin Users / Policies panels: drop phantom permission rows referencing a just-removed bucket (cache was not cascading the removal through `Policy.permissions` / `ManagedRole.permissions`) ([#4855](https://github.com/quiltdata/quilt/pull/4855))
2122
- [Changed] Migrate user-facing bucket queries to the new role-scoped `buckets` / `bucket` GraphQL type; admins in a scoped managed role now see only role-permitted buckets in navbar / listings / deep-links (admin panel unchanged) ([#4839](https://github.com/quiltdata/quilt/pull/4839))
2223
- [Fixed] Refresh navbar bucket selector after bucket removal and after policy / role / user-role edits that change the caller's managed-role bucket set ([#4839](https://github.com/quiltdata/quilt/pull/4839))
2324
- [Added] HubSpot tracking ([#4807](https://github.com/quiltdata/quilt/pull/4807))

catalog/app/utils/GraphQL/Provider.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const USERS_QUERY = urql.gql`{ admin { user { list { name } } } }`
2121
const DEFAULT_ROLE_QUERY = urql.gql`{ defaultRole { id } }`
2222

2323
// Invalidate all cached variants of a root Query field (args-aware).
24+
// Marks cached variants stale so the next read refetches; does NOT
25+
// reliably notify active subscribers on urql 2.x + graphcache 4.x.
26+
// Use refetchRootField when a subscriber must see the new data
27+
// without re-navigation.
2428
function invalidateRootField(cache: GraphCache.Cache, fieldName: string) {
2529
for (const f of cache.inspectFields('Query')) {
2630
if (f.fieldName === fieldName) {
@@ -221,6 +225,29 @@ export default function GraphQLProvider({ children }: React.PropsWithChildren<{}
221225
R.evolve({ buckets: R.reject(R.propEq('name', vars.name)) }),
222226
)
223227
cache.invalidate({ __typename: 'Bucket', name: vars.name })
228+
// Server cascade-deletes the PolicyBucketPermission /
229+
// RoleBucketPermission rows for the removed bucket, but the
230+
// cached entries (keyed {bucketName}/{id}) are not reachable
231+
// via the Bucket-entity invalidation above — strip them from
232+
// every cached Policy.permissions and ManagedRole.permissions
233+
// array.
234+
const stripBucket = R.reject(R.pathEq(['bucket', 'name'], vars.name))
235+
cache.updateQuery(
236+
{
237+
query: urql.gql`{ policies { id permissions { bucket { name } } } }`,
238+
},
239+
R.evolve({
240+
policies: R.map(R.evolve({ permissions: stripBucket })),
241+
}),
242+
)
243+
cache.updateQuery(
244+
{
245+
query: urql.gql`{ roles { id ... on ManagedRole { permissions { bucket { name } } } } }`,
246+
},
247+
R.evolve({
248+
roles: R.map(R.evolve({ permissions: stripBucket })),
249+
}),
250+
)
224251
},
225252
policyCreateManaged: (result, _vars, cache) => {
226253
const policy = result.policyCreateManaged as any

0 commit comments

Comments
 (0)