fix(plg): ensure invite flow is enabled only for embedded UI#63466
Conversation
chrsmith
left a comment
There was a problem hiding this comment.
Some nitpicks and design suggestions, but overall I think this is a big improvement in terms of readability. Although we can continue to improve things. (As the Cody Management page is definitely more complicated than you would think.)
|
|
||
| const isUserOnProTier = subscription.plan === CodySubscriptionPlan.PRO | ||
| const inviteNodes: InviteNodes = ((): InviteNodes => { | ||
| const nodes: InviteNodes = { banner: null, link: null, form: null } |
There was a problem hiding this comment.
As a way to make this code easier to understand, what do you think about the following:
Don't introduce a nodes object that is mutated during the execution of the function. (Since that "temporarily couples" things, and the sequence of code blocks is important.) For example, there are three places where we return nodes but all of them have a different meaning.
Instead, consider creating immutable values, and then returning an object literal. That way it is explicit what is being returned from each code path. e.g.
// Invites flow is supported only for embedded Cody UI.
if (!isEmbeddedCodyProUIEnabled()) {
return { } // All fields of InviteNodes left undefined.
}
...
const banner = ...
// Only admins can invite users and manage team.
const subscriptionSummary = subscriptionSummaryQueryResult.data
if (!subscriptionSummary || subscriptionSummary.userRole !== 'admin') {
return { banner }
}
...
return { banner, form, link }I wouldn't say this is "clearly" better, but it does have the benefit of making it much more explicit about what fields on the returned object should be set depending on the code path.
| })() | ||
|
|
||
| const pageHeaderLink: React.ReactNode = (() => { | ||
| // User is admin and can manage team and subscription - render corresponding link. |
There was a problem hiding this comment.
This logic is a little dangerous. All this code snippet has is an inviteNodes.link that is non-null. We are assuming that the user is an admin and can manage the team/subscription. (Which might not always be the case as the code evolves.)
I think we would still want to keep the code as-is (if (inviteNodes.link) return inviteNodes.link) but I would praise the comment differently:
"We determined that we should have a link on the inviteNodes, so we will show it. i.e. pageHeaderLink isn't responsible for keeping track of user state and roles or what not. Instead, we make that entirely the responsibility of whatever is building the inviteNodes object. Does that make sense?
- Some things need to have our business logic and figure out "what to do"
- Other things, just take that data and do what is instructed (e.g. showing links/buttons, etc.)
We cannot always make such a clear cut division like this. (e.g. the need for checking on the subscription.plan value below.) But the meta point is that as we break down the UI into smaller components, functions, etc. I would argue that a good design would be to find ways where we can remove the "coupling" between the specific of user stage from just "building the DOM elements as described by object XXX".
|
Also, since we want to get this fix in soon, I don't want to harp on this. But it would be really nice if we could have some unit tests or a Storybook page to sanity check this component and these permutations of user state, subscription status, etc. in the future. |
…aph into ty/gate-invite-flow
|
Thanks for a thorough review, @chrsmith! |
Makes the invite flow on "/cody/manage" page dependent on the
dotcom.codyProConfig.useEmbeddedUIsite config param.Previously we didn't have a check for this site config param on this page (to be precise, it's predecessor, see https://github.com/sourcegraph/sourcegraph/pull/63442).
useEmbeddedUIfalsefalsefalsetruetruetrueTest plan
dotcom.codyProConfig.useEmbeddedUIsite config param, log in with users with different team roles (member/admin) and check results with the table above.