Skip to content

feat(seer): Implement bulk editing for repo code-review settings#104908

Merged
ryan953 merged 2 commits intomasterfrom
ryan953/seer-settings-bulk-code-review
Dec 12, 2025
Merged

feat(seer): Implement bulk editing for repo code-review settings#104908
ryan953 merged 2 commits intomasterfrom
ryan953/seer-settings-bulk-code-review

Conversation

@ryan953
Copy link
Copy Markdown
Member

@ryan953 ryan953 commented Dec 12, 2025

This is a followup to #104886 which adds some complexity, but hoists some state up into the main Table component to share it better with each row.

Now we can bulk-edit items in the list in addition to setting things specifically.
Doing a bulk-edit will result in re-fetches for each item in the list. So if someone clicks to select all items it could be crazy as we re-fetch everything :(
For bulk editing a few items this works great. I don't expect people will be hammering this page with changes, so a little jank is going to be ok to start with.

Screen.Recording.2025-12-12.at.3.24.21.PM.mov

@ryan953 ryan953 requested review from a team and scttcper December 12, 2025 23:27
@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Dec 12, 2025
onSuccess: mutations => {
setMutations(prev => {
return mutations.reduce(
(map, mutation) => ({...map, [mutation.id]: mutation}),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid spread in reduce

@ryan953 ryan953 enabled auto-merge (squash) December 12, 2025 23:38
Comment on lines +60 to +70
return updated;
});
},
onSettled: mutations => {
(mutations ?? []).forEach(mutation => {
queryClient.invalidateQueries({
queryKey: getRepositoryWithSettingsQueryKey(organization, mutation.id),
});
});
},
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The mutationData state is not cleared after a mutation, causing mutated repository rows to permanently and unnecessarily refetch data from the API on re-renders or window focus.
Severity: MEDIUM | Confidence: High

🔍 Detailed Analysis

After a repository's settings are mutated, an entry is added to the mutationData state object but is never removed. The onSuccess callback adds the entry, which correctly triggers a refetch by enabling the useRepositoryWithSettings hook. However, the onSettled callback, which runs after the mutation, only invalidates the query cache and fails to clear the corresponding entry from mutationData. Because the hook's enabled condition (mutationData[initialRepository.id] !== undefined) remains permanently true for that repository, it will continue to refetch from the API on every component re-render or window refocus for the remainder of the component's lifecycle, creating unnecessary network requests.

💡 Suggested Fix

Update the onSettled callback in the mutation to remove the entry from the mutationData state after the query has been invalidated. For example: setMutations(prev => {const {[mutation.id]: _, ...rest} = prev; return rest;}).

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location:
static/gsApp/views/seerAutomation/components/repoTable/seerRepoTable.tsx#L49-L70

Potential issue: After a repository's settings are mutated, an entry is added to the
`mutationData` state object but is never removed. The `onSuccess` callback adds the
entry, which correctly triggers a refetch by enabling the `useRepositoryWithSettings`
hook. However, the `onSettled` callback, which runs after the mutation, only invalidates
the query cache and fails to clear the corresponding entry from `mutationData`. Because
the hook's `enabled` condition (`mutationData[initialRepository.id] !== undefined`)
remains permanently true for that repository, it will continue to refetch from the API
on every component re-render or window refocus for the remainder of the component's
lifecycle, creating unnecessary network requests.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 7474880

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Accessing `.length` on union type without type guard

The condition selectedIds.length === repositories.length accesses .length on selectedIds which has type 'all' | string[]. When selectedIds === 'all', this returns 3 (string length) rather than the count of selected items. Currently this is protected by short-circuit evaluation with isAllSelected === true being checked first, but the code is fragile—if conditions are reordered or the relationship between selectedIds and isAllSelected changes, incorrect behavior would occur. Following the pattern used elsewhere in the codebase (e.g., useBulkEditFeedbacks.tsx), the code could explicitly check Array.isArray(selectedIds) before accessing .length.

static/gsApp/views/seerAutomation/components/repoTable/seerRepoTableHeader.tsx#L195-L196

onChange={() => {
if (isAllSelected === true || selectedIds.length === repositories.length) {

Fix in Cursor Fix in Web


@ryan953 ryan953 merged commit 019f5bf into master Dec 12, 2025
48 checks passed
@ryan953 ryan953 deleted the ryan953/seer-settings-bulk-code-review branch December 12, 2025 23:48
Comment on lines +54 to +55
// TODO: we should not be overriding the existing code review triggers for the repositories
codeReviewTriggers: DEFAULT_CODE_REVIEW_TRIGGERS,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting fixed up in #104912

ryan953 added a commit that referenced this pull request Dec 16, 2025
…ings (#104912)

Followup to #104908
Depends on #104918

Currently the backend expects that all fields will be passed to the
bulk-edit endpoint, but that's only true in the wizard context, when
we're creating everything for the first time:
https://github.com/getsentry/sentry/blob/master/src/sentry/integrations/api/endpoints/organization_repository_settings.py#L16-L41

On the settings page people can bulk-update fields, one field at a time.
@github-actions github-actions bot locked and limited conversation to collaborators Dec 28, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants