Skip to content

feat(Coder plugin): expose Coder SDK to Backstage end-users #132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 4, 2024

Conversation

Parkreiner
Copy link
Member

@Parkreiner Parkreiner commented May 28, 2024

Part of #112

Changes made

  • Created useCoderQuery hook that simplifies wiring up all the Coder plugin logic to useQuery
  • Added an initial set of tests for the hook
  • Gave users the ability to disable the auth fallback UI altogether
  • Updated some of the test helpers

Example usage

This is from the end-user's perspective (useEndUserCoderAuth will already have been renamed to just useCoderAuth)

// Without useCoderQuery
const { sdk } = useCoderSdk();
const { isAuthenticated } = useCoderAuth();
const query = useQuery({
  queryKey: [CODER_QUERY_KEY_PREFIX, "workspaces"],
  queryFn: () => sdk.getWorkspaces({ q: "owner:me" }),
  enabled: isAuthenticated,
});

// This is fully equivalent and has additional properties get updated based on auth status
const query2 = useCoderQuery({
  queryKey: ["workspaces"],
  queryFn: ({ sdk }) => sdk.getWorkspaces({ q: "owner:me" })
});

Notes

  • I originally wanted to create a useCoderMutation hook, too, but decided to put it on the back-burner for now because:
    • I fully expect queries to be the way that users interact with the API +75% of the time
    • On Thursday, I'm going to start working on the Windows RDP module, and I knew I wouldn't have time to get both hooks ready and tested (need to test them individually and together)
    • We still need some idea of what flows devs are going to be using with these hooks, so best not to commit to some false assumptions until we have more concrete user data
  • Will publish a new PR with documentation changes soon

@Parkreiner Parkreiner self-assigned this May 28, 2024
@Parkreiner Parkreiner marked this pull request as ready for review May 30, 2024 23:54
@Parkreiner Parkreiner changed the title feat(Coder plugin): improve developer ergonomics of using Coder SDK feat(Coder plugin): expose Coder SDK to Backstage end-users May 31, 2024
@Parkreiner Parkreiner requested a review from code-asher May 31, 2024 17:21
Copy link
Member

@code-asher code-asher left a comment

Choose a reason for hiding this comment

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

Nice! One thing I wonder about, from a user's perspective, is that the connection between the sdk and auth is not immediately obvious. Like, coming from other rest sdks, I think I would expect something like this: scratch that, see below

const sdk = makeCoderSdk(url, "token")
sdk.getWorkspaces()

Or to use Coder's auth provider:

const { token } = useCoderAuth() // Triggers the auth popup if no token yet
const sdk = makeCoderSdk(url, token)
sdk.getWorkspaces()

BackstageHttpError.isInstance(queryError) &&
queryError.status === 401;

if (!shouldRevalidate) {
return;
}

isRevalidatingToken = true;
isRevalidating = true;
await queryClient.refetchQueries({ queryKey: sharedAuthQueryKey });
Copy link
Member

Choose a reason for hiding this comment

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

Not related to this PR, just a question because I think I might have skipped over this before or I just forgot.

This is not making the fetches again on a 401, right? That would just result in another 401. Is it to clear out the data or something?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's sort of both? The logic spies on any 401s made for the currently-loaded query client, regardless of whether the 401 was for a Coder query or not.

Since we let the user supply their own Query client, the thinking was that even if a 401 triggers on a non-Coder query, it might help to revalidate the token, to handle cases like bad internet connections

Copy link
Member Author

Choose a reason for hiding this comment

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

One of the limitations of storing the auth validity state with React Query is that normally, the current state is tied to one specific API function, with one specific query key

So React Query doesn't automatically know that when a non-auth query function errors out, that the underlying auth is probably invalid, too.

Though now that I think about it, I should be able to update the auth state directly when a Coder query fails. Let me see if I can't get that fixed really quick

Copy link
Member Author

@Parkreiner Parkreiner Jun 3, 2024

Choose a reason for hiding this comment

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

Think I have this improved now!
Revisiting the logic also made me realize that we don't really need the custom BackstageHttpError type anymore, now that we have Axios (it exposes a lot of the same properties)

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, never mind. Some of the changes are causing the existing tests to fail.
I'll open a new issue about this, but just to keep the scope of this PR small, I went ahead and reverted the changes

Copy link
Member

Choose a reason for hiding this comment

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

handle cases like bad internet connections

I think they would get some network error rather than a 401. Getting a 401 should mean the server responded and the token is well and truly invalid, and will never be valid again (at least if the token generating system is done correctly hahaha).

Although, it is interesting to consider the case where maybe they have a proxy in front of Coder, and it is the proxy that returned a 401 and not Coder, and the token is actually still valid. Buuuuut I guess even in that case the second query will still always fail with a 401, since a 401 is not automatically recoverable.

regardless of whether the 401 was for a Coder query or not

Ohhhhhhh I see, they could use it to make a request to some other service, and that service might give a 401? That does sound tricky! I see the use of the revalidate for this case but I agree it would be cool if it could be scoped to Coder queries.

Comment on lines +2 to +3
* @file Defines a couple of wrappers over React Query/Tanstack Query that make
* it easier to use the Coder SDK within UI logic.
Copy link
Member

Choose a reason for hiding this comment

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

Do most Backstage users use React Query? I worry we are abstracting too early.

If we just expose the base useCoderSdk and let users hook it up however they need, that seems most ideal to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm sure some of them do, but Backstage's default recommendations don't include it

I cover this in the README PR, but there are a lot of limitations around useEffect/useAsync, so I figured it would help to provide an easier way to bring RQ into the app. The plugin is still going to expose the SDK directly for people who want to wire things up in a more custom way

@code-asher
Copy link
Member

Oh yeah wait the whole reason is to make it Backstage-y with the useApi stuff. Yeah gotta be the way you have it, I think.

Base automatically changed from mes/vendored-sdk-integration to main June 3, 2024 14:23
@Parkreiner
Copy link
Member Author

@code-asher Yeah, I tried to improve the co-location for the auth/SDK stuff to make their relationship a little more obvious, but when I tried it, I think the changes made things more confusing. I tried adding some auth-based helpers to useCoderSdk, but then it felt like that was blurring some of the lines for the responsibilities for it and useCoderAuth

I agree that the SDK setup flow you posted would be nice, but I don't know how to make that play nicely with React and Backstage. We don't have to do things the Backstage way – we could make it so that the user has to initialize the Coder Client themselves (making sure it's initialized outside a render function), and then feed that to the rest of the app via React Context. That's not the Backstage way, but it is the React way, which is good enough for me

The main thing I'm not 100% sure on is that, with the client being dependent on other Backstage APIs, you might have to jump through a ton of hoops to get feed those into the Coder Client since you lose access to API factories. It should be doable, but I'm not 100% sure if that might add more confusion, or might make the code even more complex

@Parkreiner Parkreiner force-pushed the mes/vendored-sdk-helpers branch from eabccd5 to a67fbcf Compare June 3, 2024 15:57
Copy link
Member

@code-asher code-asher left a comment

Choose a reason for hiding this comment

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

Yeah, I think useApi like you have it now is the right abstraction to use for this.

Thinking out loud, maybe the relationship would be clearer if we had something like:

        return new CoderClient({
          apis: { urlSync, identityApi, coderAuthApi },
        });

And then CoderClient calls coderAuthApi.token() or something.

It feels a bit more Backstage-y to me over the React-ish way we have it now with the auth provider context. But idk if it is worth thinking about right now. Maybe when we revisit with oauth, since that will be a Backstage API as well. Could be cool if you could decide whether to slot that in or the current version with the pasting.

I do still wonder about the additional React Query abstraction, but going to defer to you since you know first-hand the pain users will experience doing it themselves. 😱

@Parkreiner
Copy link
Member Author

Parkreiner commented Jun 4, 2024

@code-asher Yeah, this is definitely one area where I wish we had more active users to get feedback from. I definitely don't expect this to be the final design, but it feels like exposing the Coder API at all is going to a good way to entice people to try out the plugin, and get the feedback on how to make this better

I just wish that, for a platform built with React, Backstage were integrated with it a little bit better. The fact that we have to weigh the tradeoffs between React-isms and Backstage-isms doesn't feel super great

@Parkreiner Parkreiner merged commit 251214e into main Jun 4, 2024
2 checks passed
@Parkreiner Parkreiner deleted the mes/vendored-sdk-helpers branch June 4, 2024 14:55
@code-asher
Copy link
Member

I just wish that, for a platform built with React, Backstage were integrated with it a little bit better. The fact that we have to weigh the tradeoffs between React-isms and Backstage-isms doesn't feel super great

This 100%, could not agree more!!

@Parkreiner Parkreiner restored the mes/vendored-sdk-helpers branch June 11, 2024 03:36
@Parkreiner Parkreiner deleted the mes/vendored-sdk-helpers branch June 17, 2024 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants