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

Migrate "New Cody Subscription" page#62322

Merged
chrsmith merged 9 commits into
mainfrom
chrsmith/new-cody-subscription-page
May 7, 2024
Merged

Migrate "New Cody Subscription" page#62322
chrsmith merged 9 commits into
mainfrom
chrsmith/new-cody-subscription-page

Conversation

@chrsmith

@chrsmith chrsmith commented Apr 30, 2024

Copy link
Copy Markdown
Contributor

This PR migrates the "New Cody Subscription" page from accounts.sourcegraph.com to sourcegraph.com, with it being served at /cody/manage/subscription/new. (My next step is to add the "Manage Subscription" page at /cody/manage/subscription, to support /cody/manage/team or other things down the road.)

The React code is pretty much a copy of what is in the sourcegraph/self-serve-cody repo. With some sg/sg specific idioms added. (e.g. wrapping the component in withAuthorizedUser(...) so it will automatically redirect the user to login if needed.)

A few things of note:

  • The page will only get registered IFF the site configuration has the codyProConfig setting available. (So this won't be enabled on sourcegraph.com, or be visible on enterprise installations.)
  • The page also requires that the calling user has the cody-enable-embedded-cody-pro-ui feature flag.
  • The stripe-js dependency was added to the client/web/package.json. My understanding is that because the route handler is loaded lazily, this won't require any new downloads when the browser doesn't need the Stripe bits. But the general thinking is that we will try to isolate the Cody Pro-specific UI into its own NPM package, to better isolate it from the "regular Cody Pro" UI. And any other Cody Enterprise-specific UI that we have in the future. But there is a bit too much ambiguity in what we want there, to do that splitting/refactoring just yet.

Next Steps

With these changes you can complete the new Cody Pro subscription checkout process. But we will need to update the /cody/manage page to support cases where the subscription was just created. (I'll sync up with Rob about maybe having an intersitular page, like we have with /cody/welcome on accounts.sourcegraph.com today. Or maybe we'll just add a "toast" notification or something from /cody/manage.)

But other than that, the next (and final) step of migrating the Cody Pro UI to sourcegraph.com is just migrating the "Manage Subscription" page..

Test Plan

I tested things manually, but do plan on adding some proper automated tests for the react bits. However, there isn't much we can do for the checkout experience specifically, since the UI is all owned by Stripe and served within an iframe. (So there isn't really any way to configure Storybook to do that in CI/CD.)

@cla-bot cla-bot Bot added the cla-signed label Apr 30, 2024
@chrsmith chrsmith marked this pull request as draft April 30, 2024 23:59
@chrsmith chrsmith force-pushed the chrsmith/new-cody-subscription-page branch 3 times, most recently from c47b66c to f1eef4b Compare May 2, 2024 17:24
@chrsmith chrsmith requested review from a team and vdavid May 2, 2024 17:46
@chrsmith chrsmith changed the title WIP New Cody Subscription page Migrate "New Cody Subscription" page May 2, 2024
@chrsmith chrsmith marked this pull request as ready for review May 2, 2024 17:52
@chrsmith chrsmith requested a review from eseliger May 2, 2024 18:34

@eseliger eseliger left a comment

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.

Left a few questions and suggestions inline, I don't see any big issues with this change, and love that we get to use Wildcard here now.

Comment thread client/web/package.json Outdated
Comment on lines 50 to 51

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.

I saw your comment in slack on this, but I still think that we shouldn't add the dependency here, and in /package.json instead.

There are a lot of dependencies in there already that are either web, shared, or something else only, but imported to all of them.

That prevents that we have to make sure not to have two objects of the same name, but no actual equality because they're from different versions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am 100% in agreement that we want to move this new dependency into the narrowest scope possible. So if /package.json is the right place to put it, awesome!

But for my own understanding, what's the difference between client/web/package.json and /package.json? Both have the description "The Sourcegraph web app", and I would have assumed that the "more general one" would be in the root of the repository. And that the package.json file just for the Sourcegraph web app would be in client/web... but I take it that isn't the case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Also, should I ignore this warning? Sounds scary. But then again, I don't quite know what we use the root package.json file for.

% pnpm install @stripe/sourcegraph-js
 ERR_PNPM_ADDING_TO_ROOT  Running this command will add the dependency to the workspace root, which might not be what you want
- if you really meant it, make it explicit by running this command again with the -w flag (or --workspace-root). If you don't
- want to see this warning anymore, you may set the ignore-workspace-root-check setting to true.

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.

I'd be curious about the reason for not currently having any non-workspace dependencies in client/web/package.json. I don't know about any particular best practice that would oppose it, but I'm sure we have some reason, and our current use case doesn't seem that special compared to what we normally do. Were people just lazy and put everything to /package.json? I kinda doubt it.

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.

Having multiple versions of the same dependency is valid in JS land, but can cause issues like instanceof checks suddenly failing. It can also cause the bundle to become much bigger because you suddenly import multiple versions.

I do agree with yall that we should probably use packages by workspace and isolate those more, but since we don't do that today, for consistency I think we should add to the root package. I am not sure why pnpm complains about that though, this is how we always did it :/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I do agree with yall that we should probably use packages by workspace and isolate those more, but since we don't do that today, for consistency I think we should add to the root package ... this is how we always did it :/

Sounds good. I'll defer to your expertise here.

Comment thread client/web/src/cody/management/subscription/new/CodyProCheckoutForm.tsx Outdated
Comment thread client/web/src/cody/management/subscription/new/CodyProCheckoutForm.tsx Outdated

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.

I'm surprised we don't have a helper that wraps fetch nicely and handles auth etc, but I couldn't find one in our codebase, so 🤷

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well, don't worry. We will have a helper that wraps fetch nicely and handles auth... since we'll be adding it a follow-up PR as we add more instances where we need to call the SSC backend through this .api/ssc/proxy endpoint.

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.

Also nit: "resp" is pretty Go-ish. "response" is idiomatic. (If you don't believe me, search our front-end codebase, "response" wins by a landslide)

Comment on lines 15 to 17

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.

We have a container component that has a standardized look, could we use that, even if it's slightly different than this one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was 100% cargo culted from the CodyManagementPage.module.scss. There is no reason to use a slightly different one. So I'll actually just remove it entirely, since removing the .module.scss file entirely doesn't seem to make any changes, and we only were using the .container property.

https://github.com/sourcegraph/sourcegraph/blob/main/client/web/src/cody/management/CodyManagementPage.module.scss#L15

Comment thread client/web/src/cody/management/subscription/new/NewCodyProSubscriptionPage.tsx Outdated
Comment thread client/web/src/cody/management/subscription/new/NewCodyProSubscriptionPage.tsx Outdated
Comment thread client/web/src/cody/management/subscription/new/NewCodyProSubscriptionPage.tsx Outdated
Comment thread schema/cody_pro_util.go Outdated

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.

I would inline these types in cmd/frontend/internal/app/jscontext/jscontext.go, it looks like this code has nothing to do with the schema generator that creates most contents in this package

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That sounds reasonable, so I tried to make that change. But by inlining the type, it means we cannot create an instance of it when we need to. (Since the FrontendCodyConfig should only be non-nil IFF dotcom and dotcom.codyProConfig config values are set.

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.

Oh I didn't mean literal inlining (sorry - this term is overloaded), I meant just moving the code in this file at the end of jscontext.go :)

Comment thread client/web/src/routes.tsx Outdated
Comment on lines 437 to 440

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.

Does that work? I would think condition is only evaluated once and while the feature flag state hasn't been loaded yet from the server, it would default to false (and the second entry in the return array would indicate it's still loading).

@vdavid vdavid May 3, 2024

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.

Also, I don't think you can (or even should be allowed to) use React hooks outside of components. Even if React/TS allows you, this looks like an anti-pattern. I'd just rely on the config and leave it at that. Then you can check for the feature flag in NewCodyProSubscriptionPage.

@chrsmith chrsmith May 6, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Does that work?

It certainly works locally 🤷 . But if we "should" do this, is another question entirely.

I'm happy to remove it. But in case you had forgotten David, I literally added this feature flag because you and Naman insisted upon it. And initially started with using site-config to gate these pages 😠.

Relying on the feature flag within the react component/page is insufficient, because unless these conditions are met we don't want the page to even load. (And not, try to load, but report an error or render a blank page instead.) Whereas having the check at the router-layer, provides the "regular" 404 page experience, doesn't require loading the additional JS bundle, etc.

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.

Interesting! Also with slow network enabled? I would've thought that the initial loading state would be problematic here 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤷 No, just running locally. So it could be that things happen to be "so fast" I'm not seeing this failure. Or, perhaps it is totally broken, but I'm just not noticing because the page reloads to quickly or something?

But I 100% believe you that requiring some sort of async-data within the URL routing is a bad idea. So I'm happy to make this change.

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.

unless these conditions are met we don't want the page to even load

@chrsmith Why is that? I don't see any harm done if we start loading the page and then show an error or a blank page while this feature is unannounced. Do you see a scenario where UX suffers because you move the check from the routing layer to the Page component?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do you see a scenario where UX suffers because you move the check from the routing layer to the Page component?

Yes, this is very much impacts the user experience.

If the router doesn't recognize the URL path, then it serves a pretty nice 404 error page. If we pass that responsibility onto the component then all bets are off.

Maybe the component is built "correctly", and renders the same 404 page. So the UX is identical. Maybe it doesn't, so some 404 pages look weird. Or perhaps the component doesn't handle this well at all, and just throws an error.

Ultimately, if we remove the requirement here from the specific components (so it's one fewer thing they need to implement in order to be "correct"), and put that logic into the router. Then the overall system is easier to reason about and maintain.

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.

It looks like the setClientSecret and setErrorDetails dependencies are missing from this useEffect call 🤔
Did you manage to run this? Maybe it only causes a linter error, I'm not sure, my IDE pretty much keeps this list auto-updated.

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.

I think it's fine to leave setters out of there because they are known to be immutable. But that requires to know it's a setState setter so I bet that's why autocomplete for that won't consider it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe I ran the linger locally, and saw this pass in the CI/CD. But regardless, I agree we should add these as explicit dependencies. (Even if they currently happen to be immutable... so unless the linter complains that the are added. I'll make the change.)

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.

Nit: "Opts" is pretty Go-ish. "Options" is idiomatic.

@chrsmith chrsmith force-pushed the chrsmith/new-cody-subscription-page branch from 5f42cec to 843c507 Compare May 6, 2024 19:43
@chrsmith

chrsmith commented May 6, 2024

Copy link
Copy Markdown
Contributor Author

@vdavid , @eseliger PTAL. In particular, are there any other important things that we should gate on checking this PR in?

If there are minor things I'd be happy to address that in a smaller follow-up PR.

Comment on lines +11 to +17
export function useEmbeddedCodyProUi(): boolean {
const codyProConfig = window.context.frontendCodyProConfig
if (codyProConfig && codyProConfig.stripePublishableKey) {
return true
}
return false
}

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.

Nit: I'd simplify this to:

Suggested change
export function useEmbeddedCodyProUi(): boolean {
const codyProConfig = window.context.frontendCodyProConfig
if (codyProConfig && codyProConfig.stripePublishableKey) {
return true
}
return false
}
export const useEmbeddedCodyProUi = (): boolean =>
!!(window.context.frontendCodyProConfig && window.context.frontendCodyProConfig.stripePublishableKey)

@vdavid vdavid left a comment

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.

Ship it!

@chrsmith chrsmith merged commit 992930f into main May 7, 2024
@chrsmith chrsmith deleted the chrsmith/new-cody-subscription-page branch May 7, 2024 16:14
This was referenced May 7, 2024
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.

3 participants