Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

feat(plg): Use react-query for team management #63267

Merged
merged 24 commits into from
Jun 19, 2024

Conversation

vdavid
Copy link
Contributor

@vdavid vdavid commented Jun 14, 2024

See the issue for context and the problem definition.

This PR uses react-query for data access, instead of the previous, custom solution.

To code reviewers: Probably easiest to review commit by commit. Not too fat PR as a whole either, but I think it's even easier by the small chunks.

Test plan

I've manually tested that the team management page auto-updates upon various changes:

  • Creating a new invite (it appears in the list)
  • Revoking an invite (it disappears from the list)
  • Making a team member an admin
  • Revoking admin right
  • Deleting a user

@cla-bot cla-bot bot added the cla-signed label Jun 14, 2024
@vdavid vdavid force-pushed the dv/use-react-query-for-team-management branch from 46ba421 to 102a073 Compare June 14, 2024 21:25
@vdavid vdavid marked this pull request as ready for review June 14, 2024 21:27
@vdavid vdavid requested review from taras-yemets and a team June 14, 2024 21:27
const queryClient = useQueryClient()
return useMutation({
mutationFn: async requestBody => callCodyProApi(Client.updateTeamMember(requestBody)),
onSettled: () => queryClient.invalidateQueries({ queryKey: queryKeys.teams.teamMembers() }),
Copy link
Contributor

Choose a reason for hiding this comment

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

If the request to update fails, do we still need to invalidate team members?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, as the mutationFn resolves the updated team members list, same as get team members call
https:/sourcegraph/sourcegraph/blob/cc1cd46cfb9acc3225cd9139c5f29a53e463adf6/client/web/src/cody/management/api/client.ts#L51-L57
instead of invalidating team members in React Query cache, we can update it directly, e.g. like here
https:/sourcegraph/sourcegraph/blob/cc1cd46cfb9acc3225cd9139c5f29a53e463adf6/client/web/src/cody/management/api/react-query/subscriptions.ts#L51-L60

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the request to update fails, do we still need to invalidate team members?

It was not a conscious decision, but now after thinking about it, I think it'd hurt us more if we didn't do this.
Like, what if there was an error for some reason but the update still affected the DB?
So it feels safer to invalidate it regardless.
It's a rare edge case, so I'd not think a lot about it.

Also, as the mutationFn resolves the updated team members list, same as get team members call

Thanks, fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, sorry, I misread the second suggestion at first. Good idea, I'll do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TBH, I think we don't need the invalidation line. Am I right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this what you meant, @taras-yemets? https:/sourcegraph/sourcegraph/commit/9311bba7a6c8676beb1e378d3a3d481c57433c3e

onSuccess: (data: TeamMember[]) => {
  queryClient.setQueryData(queryKeys.teams.teamMembers(), data)
}

We already updated the cache record with the key queryKeys.teams.teamMembers(), no need to invalidate it (don't return anything from success handler).

I think it'd hurt us more if we didn't do this.
Like, what if there was an error for some reason but the update still affected the DB?

I expect backend to handle unsuccessful DB transactions (e.g., rollback). But I agree, it doesn't hurt.

TBH, I think we don't need the invalidation line. Am I right?

If we remove the team member (or change their role) in this mutation, useTeamMembers query needs to be invalidated. Otherwise it still keeps the old state with the team member existing (or with "old" role). WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I see. Is this even better, then? https:/sourcegraph/sourcegraph/pull/63267/commits/f27539367a4793a6d8ea55f376ac968449d4f8de
(I've also updated the component to simplify it, which I forgot earlier.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see. Is this even better, then? https:/sourcegraph/sourcegraph/commit/f27539367a4793a6d8ea55f376ac968449d4f8de

👍🏻

I've also updated the component to simplify it

someMutation.mutateAsync.call seems overcomplicated to me (more on it in https:/sourcegraph/sourcegraph/pull/63227#discussion_r1644225613).

const queryClient = useQueryClient()
return useMutation({
mutationFn: async requestBody => callCodyProApi(Client.sendInvite(requestBody)),
onSuccess: () => queryClient.invalidateQueries({ queryKey: queryKeys.invites.teamInvites() }),
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The link is broken but I guess you meant this? https:/sourcegraph/sourcegraph/pull/63267/commits/65b09e80b62199311ae2a196b580732a6075d16f
(the resend endpoint returns an empty response, not the invite)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it refers to updating the cache from response instead of invalidating the query (similar to https:/sourcegraph/sourcegraph/pull/63267#discussion_r1642617096).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -3,8 +3,8 @@ export type TeamRole = 'member' | 'admin'
export interface TeamMember {
accountId: string
displayName: string
email: string
Copy link
Contributor

Choose a reason for hiding this comment

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

Just checking: do we have an accompanying PR to SSC repo exposing this field?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should already expose the field because the old approach was already using it. I'll double-check.

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we don't expose it: convert function, type definition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, weird. I'll add it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

const [invitesResponse, invitesDataError] = useSSCQuery<{ invites: TeamInvite[] }>('/team/current/invites')
const teamInvites = invitesResponse?.invites
const subscriptionQueryResult = useCurrentSubscription()
const isPro = subscriptionQueryResult.data?.subscriptionStatus !== 'canceled'
Copy link
Contributor

Choose a reason for hiding this comment

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

isPro may also be true if subscriptionQueryResult is loading or errored out.
Later in useEffect we use this value to decide whether we need to navigate to "/cody/manage" page. I think the comment may helpf to inform the reader that "data is loaded, we know user's plan, it's not on a Pro, redirect them to another page". WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -42,7 +45,7 @@ export const InviteUsers: React.FunctionComponent<InviteUsersProps> = ({
try {
const responses = await Promise.all(
emailAddresses.map(emailAddress =>
requestSSC('/team/current/invites', 'POST', { email: emailAddress, role: 'member' })
sendInviteMutation.mutateAsync.call(undefined, { email: emailAddress, role: 'member' })
)
)
if (responses.some(response => response.status !== 200)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to revisit error-handling logic here after we switch custom mutation hooks?

sendInviteMutation's mutateFn uses callCodyProApi which throws on non-2xx responses:
https:/sourcegraph/sourcegraph/blob/cc1cd46cfb9acc3225cd9139c5f29a53e463adf6/client/web/src/cody/management/api/react-query/callCodyProApi.ts#L51-L61

Promise.all rejects in any of the input promises rejects, thus we won't likely get to the following line:

if (responses.some(response => response.status !== 200)) {

and will get to the catch block (disclaimer: it's an assumption, I haven't run the code myself).

Also, some of the invites may be sent successfully before one fails. We may want to keep track of what invites exactly failed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we still need to keep invitesSendingStatus and invitesSendingErrorMessage in state? Can we derive these values from sendInviteMutation?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we derive invitesSentCoiunt from emailAddresses.length and sendInviteMutation status?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we need to revisit error-handling logic here after we switch custom mutation hooks?
Also, some of the invites may be sent successfully before one fails. We may want to keep track of what invites exactly failed.

Completely rewrote error handling here: https:/sourcegraph/sourcegraph/pull/63267/commits/22ce799f3485e47c06d01d858e1892eac36400fc

Do we still need to keep invitesSendingStatus and invitesSendingErrorMessage in state? Can we derive these values from sendInviteMutation?

Can we derive invitesSentCount from emailAddresses.length and sendInviteMutation status?

Did both in: https:/sourcegraph/sourcegraph/pull/63267/commits/be05e798a50dcb4a6d0139d832db9b0ab089f20c

@vdavid vdavid force-pushed the dv/use-react-query-for-team-management branch 4 times, most recently from 65b09e8 to bb2ab10 Compare June 19, 2024 13:39
@vdavid vdavid force-pushed the dv/use-react-query-for-team-management branch 2 times, most recently from 3289c87 to 1f0e9cb Compare June 19, 2024 15:17
@vdavid vdavid force-pushed the dv/use-react-query-for-team-management branch from 1f0e9cb to ce73390 Compare June 19, 2024 15:39
Copy link
Contributor

@taras-yemets taras-yemets left a comment

Choose a reason for hiding this comment

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

Approving to unblock.We can address remaining feedback (if any) in follow up PRs.

@vdavid vdavid merged commit 76a1c65 into main Jun 19, 2024
11 checks passed
@vdavid vdavid deleted the dv/use-react-query-for-team-management branch June 19, 2024 17:05
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Team member list doesn't update after role change
2 participants