-
Notifications
You must be signed in to change notification settings - Fork 5
chore(Coder plugin): Create guide for working with the Coder SDK #133
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
Changes from all commits
Commits
Show all changes
112 commits
Select commit
Hold shift + click to select a range
a27e95e
chore: add vendored version of experimental Coder SDK
Parkreiner 9c958d3
chore: update CoderClient class to use new SDK
Parkreiner 4979067
chore: delete mock SDK
Parkreiner 5e7e01f
fix: improve data hiding for CoderSdk
Parkreiner 937f6f5
docs: update typo
Parkreiner 7e84d00
Merge branch 'mes/vendored-sdk' into mes/vendored-sdk-integration
Parkreiner 294572d
wip: commit progress on updating Coder client
Parkreiner d9626a0
wip: commit more progress on updating types
Parkreiner 1dcc13b
chore: remove valibot type definitions from global constants file
Parkreiner 692a763
chore: rename mocks file
Parkreiner 28accc8
fix: update type mismatches
Parkreiner d032768
wip: commit more update progress
Parkreiner a76db16
wip: commit progress on updating client/SDK integration
Parkreiner d22bc20
fix: get all tests passing for CoderClient
Parkreiner 08cd049
fix: update UrlSync updates
Parkreiner 2eb4987
fix: get all tests passing
Parkreiner 37645f4
chore: update all mock data to use Coder core entity mocks
Parkreiner c6cc7b7
refactor: improve co-location for useCoderWorkspacesQuery
Parkreiner 07f9cde
wip: commit progress on React Query wrappers
Parkreiner 864357d
fix: add extra helpers to useCoderSdk
Parkreiner fbe1411
Merge branch 'mes/vendored-sdk-integration' into mes/vendored-sdk-hel…
Parkreiner 67ac529
wip: add test stubs for useCoderQuery
Parkreiner 4d66cca
fix: add queryKey patching to useCoderQuery
Parkreiner 333aaa3
fix: only add queryKey prefix if it is missing
Parkreiner e0bd1a2
fix: make Coder query key prefix an opaque string
Parkreiner 3c835b4
refactor: improve ergonomics of useCoderQuery
Parkreiner 0d20670
refactor: clean up query key patching logic
Parkreiner 6bff452
chore: let users disable fallback auth UI
Parkreiner 83ee830
wip: commit progress on tests
Parkreiner df6339d
chore: update wording for clarity
Parkreiner 5df5070
fix: update import for workspaces card root
Parkreiner 5aa5759
chore: get first test passing
Parkreiner 0b49e2f
chore: add inverted promise helper
Parkreiner d50a3af
fix: make non-authenticated queries fail faster
Parkreiner ea46f3c
fix: update tests to make setup easier
Parkreiner f4ca6e8
wip: get another test passing
Parkreiner a12d4d8
chore: finish all initial tests for useCoderQuery
Parkreiner 1c11f5c
fix: tighten up types for inverted promises
Parkreiner 071d8c8
fix: more tightening
Parkreiner 5120b5b
fix: make sure queries aren't tried indefinitely by default
Parkreiner d172941
wip: commit docs progress
Parkreiner 5830624
fix: increase granularity for auth fallback behavior
Parkreiner 751ed1c
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner 883f1ab
wip: commit more docs progress
Parkreiner 67713a1
fix: establish better boundaries between hooks
Parkreiner 07e054d
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner 570cecc
wip: commit more progress
Parkreiner 4b5265c
wip: more docs progress
Parkreiner ee6129d
fix: split up auth fallback logic into three providers
Parkreiner 9d52273
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner b95865e
fix: update example code
Parkreiner 85b7942
wip: commit more progress
Parkreiner 6814af8
fix: update names for auth fallback modes
Parkreiner aa4f684
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner 032e5a9
wip: more progress
Parkreiner 5494443
fix: remove repetitive wording
Parkreiner cac828d
wip: more progress
Parkreiner 256e2bd
fix: add table of contents header
Parkreiner 4f6adb3
fix: improve granularity of expired token spy logic
Parkreiner 7327d61
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner 0a0b647
fix: prevent infinite revalidation loop
Parkreiner c2decf8
fix: clean up the cleanup logic
Parkreiner 2b97304
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner 0b60ebd
fix: update example code
Parkreiner e4411f5
fix: update header levels
Parkreiner 2b924e9
fix: make prop optional
Parkreiner f0abd9f
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner 9cf133c
chore: add warning about query client mistakes
Parkreiner dae3e61
wip: finish last code example
Parkreiner cdec143
fix: update union/intersection mismatch
Parkreiner 0fd345a
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner c07aff0
chore: finish initial version of SDK readme
Parkreiner f3e90ca
wip: make placeholders more obvious
Parkreiner 977b2eb
fix: add additional properties to hide from SDK
Parkreiner a9b24aa
Merge branch 'mes/vendored-sdk' into mes/vendored-sdk-integration
Parkreiner 9038850
Merge branch 'mes/vendored-sdk-integration' into mes/vendored-sdk-hel…
Parkreiner 69a1c7a
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner 259702e
Merge branch 'main' into mes/vendored-sdk-integration
Parkreiner 09240cc
fix: shrink down the API of useCoderSdk
Parkreiner 0cbdea7
Merge branch 'mes/vendored-sdk-integration' into mes/vendored-sdk-hel…
Parkreiner 3a8accb
update method name for clarity
Parkreiner a67fbcf
chore: removal vestigal endpoint properties
Parkreiner 26baf17
Merge branch 'mes/vendored-sdk-integration' into mes/vendored-sdk-hel…
Parkreiner cd734e3
Merge branch 'mes/vendored-sdk-helpers' into mes/vendored-sdk-readme
Parkreiner a0f4340
fix: swap public 'SDK' usage with 'API'
Parkreiner 3329f3d
fix: remove temp import
Parkreiner a56f332
fix: update exports for end-types
Parkreiner 3a5582a
fix: update query wrapper tests
Parkreiner 91185aa
wip: commit current rewrite progress
Parkreiner 5dc9035
fix: update structure of directory readme
Parkreiner 4b52bf7
wip: commit more docs progress
Parkreiner 753a5e5
chore: finish second draft of main README
Parkreiner 3402ea5
refactor: rename ejectToken to unlinkToken
Parkreiner 45eac85
refactor: reorganize readme file structure
Parkreiner 2bc6838
update details for new versions of README
Parkreiner 852226d
chore: delete first draft of the README
Parkreiner cad3e16
Merge branch 'main' into mes/vendored-sdk-readme
Parkreiner 21b4b7f
fix: remove duplicate destructuring
Parkreiner 7c03d27
fix: update duplicate exports
Parkreiner 6b58a89
fix: update semver message
Parkreiner 3c647e9
fix: remove useEffect comparison column
Parkreiner a37d9c7
fix: move custom query client into advanced section
Parkreiner 8fef874
fix: remove redundant examples
Parkreiner 2ab5836
fix: update hook overview
Parkreiner 51de0c9
fix: update formatting for advanced file
Parkreiner 0bb2b92
fix: regorganize prefix section
Parkreiner f4d05aa
chore: finish v3 of reorganization
Parkreiner d010cb6
chore: reorganize text content one last time
Parkreiner adad952
chore: group prefix examples
Parkreiner 5012b01
chore: reorganize directory readme
Parkreiner b1b05d8
chore: add image of auth fallback
Parkreiner 1ce21c9
chore: add video of auth functionality
Parkreiner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,22 @@ | ||
# Plugin API Reference – Coder for Backstage | ||
# Documentation Directory – `backstage-plugin-coder` v0.3.0 | ||
|
||
For users who need more information about how to extend and modify the Coder plugin. For general setup, please see our main [README](../README.md). | ||
This document lists core information for the Backstage Coder plugin. It is intended for users who have already set up the plugin and are looking to take it further. | ||
|
||
All documentation reflects version `v0.2.0` of the plugin. Note that breaking API changes may continue to happen for minor versions until the plugin reaches version `v1.0.0`. | ||
For general setup, please see our [main README](../README.md). | ||
|
||
## Documentation directory | ||
## Documentation listing | ||
|
||
- [Components](./components.md) | ||
- [Custom React hooks](./hooks.md) | ||
- [Important types](./types.md) | ||
### Guides | ||
|
||
- [Using the Coder API from Backstage](./guides/coder-api.md) | ||
- [Advanced use cases for the Coder API](./guides//coder-api-advanced.md) | ||
|
||
### API reference | ||
|
||
- [Components](./api-reference/components.md) | ||
- [Custom React hooks](./api-reference/hooks.md) | ||
- [Important types](./api-reference/types.md) | ||
|
||
## Notes about semantic versioning | ||
|
||
We fully intend to follow semantic versioning with the Coder plugin for Backstage. Expect some pain points as we figure out the right abstractions needed to hit version 1, but we will try to minimize breaking changes as much as possible as the library gets ironed out. |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
72 changes: 72 additions & 0 deletions
72
plugins/backstage-plugin-coder/docs/guides/coder-api-advanced.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Working with the Coder API - advanced use cases | ||
|
||
This guide covers some more use cases that you can leverage for more advanced configuration of the Coder API from within Backstage. | ||
|
||
## Changing fallback auth component behavior | ||
|
||
By default, `CoderProvider` is configured to display a fallback auth UI component when two cases are true: | ||
|
||
1. The user is not authenticated | ||
2. There are no official Coder components are being rendered to the screen. | ||
|
||
<img src="../../screenshots/auth-fallback.png" alt="The Coder auth fallback UI" /> | ||
|
||
All official Coder plugin components are configured to let the user add auth information if the user isn't already authenticated, so the fallback component only displays when there would be no other way to add the information. | ||
|
||
However, depending on your use cases, `CoderProvider` can be configured to change how it displays the fallback, based on the value of the `fallbackAuthUiMode` prop. | ||
|
||
```tsx | ||
<CoderProvider fallbackAuthUiMode="assertive"> | ||
<OtherComponents /> | ||
</CoderProvider> | ||
``` | ||
|
||
There are three values that can be set for the mode: | ||
|
||
- `restrained` (default) - The auth fallback will only display if the user is not authenticated, and there would be no other way for the user to add their auth info. | ||
- `assertive` - The auth fallback will always display when the user is not authenticated, regardless of what Coder component are on-screen. But the fallback will **not** appear if the user is authenticated. | ||
- `hidden` - The auth fallback will never appear under any circumstances. Useful if you want to create entirely custom components and don't mind wiring your auth logic manually via `useCoderAuth`. | ||
|
||
## Connecting a custom query client to the Coder plugin | ||
|
||
By default, the Coder plugin uses and manages its own query client. This works perfectly well if you aren't using React Query for any other purposes, but if you are using it throughout your Backstage deployment, it can cause issues around redundant state (e.g., not all cached data being vacated when the user logs out). | ||
|
||
To prevent this, you will need to do two things: | ||
|
||
1. Pass in your custom React Query query client into the `CoderProvider` component | ||
2. "Group" your queries with the Coder query key prefix | ||
|
||
```tsx | ||
const yourCustomQueryClient = new QueryClient(); | ||
|
||
<CoderProvider queryClient={yourCustomQueryClient}> | ||
<YourCustomComponents /> | ||
</CoderProvider>; | ||
|
||
// Ensure that all queries have the correct query key prefix | ||
import { useQuery } from '@tanstack/react-react-query'; | ||
import { | ||
CODER_QUERY_KEY_PREFIX, | ||
useCoderQuery, | ||
} from '@coder/backstage-plugin-coder'; | ||
|
||
function CustomComponent() { | ||
const query1 = useQuery({ | ||
queryKey: [CODER_QUERY_KEY_PREFIX, 'workspaces'], | ||
queryFn: () => { | ||
// Get workspaces here | ||
}, | ||
}); | ||
|
||
// useCoderQuery automatically prefixes all query keys with | ||
// CODER_QUERY_KEY_PREFIX if it's not already the first value of the array | ||
const query2 = useCoderQuery({ | ||
queryKey: ['workspaces'], | ||
queryFn: () => { | ||
// Get workspaces here | ||
}, | ||
}); | ||
|
||
return <div>Main component content</div>; | ||
} | ||
``` |
262 changes: 262 additions & 0 deletions
262
plugins/backstage-plugin-coder/docs/guides/coder-api.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
# Coder API - Quick-start guide | ||
|
||
## Overview | ||
|
||
The Coder plugin makes it easy to bring the entire Coder API into your Backstage deployment. This guide covers how to get it set up so that you can start accessing Coder from Backstage. | ||
|
||
Note: this covers the main expected use cases with the plugin. For more information and options on customizing your Backstage deployment further, see our [Advanced API guide](./coder-api-advanced.md). | ||
|
||
### Before you begin | ||
|
||
Please ensure that you have the Coder plugin fully installed before proceeding. You can find instructions for getting up and running in [our main README](../../README.md). | ||
|
||
### Important hooks for using the Coder API | ||
|
||
The Coder plugin exposes three (soon to be four) main hooks for accessing Coder plugin state and making queries/mutations | ||
|
||
- `useCoderAuth` - Provides methods and state values for interacting with your current Coder auth session from within Backstage. | ||
|
||
```tsx | ||
function SessionTokenInputForm() { | ||
const [sessionTokenDraft, setSessionTokenDraft] = useState(''); | ||
const coderAuth = useCoderAuth(); | ||
|
||
const onSubmit = (event: FormEvent<HTMLFormElement>) => { | ||
coderAuth.registerNewToken(sessionToken); | ||
setSessionTokenDraft(''); | ||
}; | ||
|
||
return ( | ||
<form onSubmit={onSubmit}> | ||
<MainFormContent /> | ||
</form> | ||
); | ||
} | ||
``` | ||
|
||
- `useCoderQuery` - Makes it simple to query data from the Coder API and share it throughout your application. | ||
|
||
```tsx | ||
function WorkspacesList() { | ||
// Return type matches the return type of React Query's useQuerys | ||
const workspacesQuery = useCoderQuery({ | ||
queryKey: ['workspaces'], | ||
queryFn: ({ coderApi }) => coderApi.getWorkspaces({ limit: 5 }), | ||
}); | ||
} | ||
``` | ||
|
||
- `useCoderMutation` (coming soon) - Makes it simple to mutate data via the Coder API. | ||
- `useCoderApi` - Exposes an object with all available Coder API methods. None of the state in this object is tied to React render logic - it can be treated as a "function bucket". Once `useCoderMutation` is available, the main value of this hook will be as an escape hatch in the rare situations where `useCoderQuery` and `useCoderMutation` don't meet your needs. Under the hood, both `useCoderQuery` and `useCoderMutation` receive their `coderApi` context value from this hook. | ||
|
||
```tsx | ||
function HealthCheckComponent() { | ||
const coderApi = useCoderApi(); | ||
|
||
const processWorkspaces = async () => { | ||
const workspacesResponse = await coderApi.getWorkspaces({ | ||
limit: 10, | ||
}); | ||
|
||
processHealthChecks(workspacesResponse.workspaces); | ||
}; | ||
} | ||
``` | ||
|
||
Internally, the Coder plugin uses [React Query/TanStack Query v4](https://tanstack.com/query/v4/docs/framework/react/overview). In fact, `useCoderQuery` and `useCoderMutation` are simply wrappers over `useQuery` and `useMutation`. Both simplify the process of wiring up the hooks' various properties to the Coder auth, while exposing a more convenient way of accessing the Coder API object. | ||
|
||
If you ever need to coordinate queries and mutations, you can use `useQueryClient` from React Query - no custom plugin-specific hook needed. | ||
|
||
The bottom of this document has examples of both queries and mutations. | ||
|
||
### Grouping queries with the Coder query key prefix | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we move this up so its closer to the hook section |
||
|
||
The plugin exposes a `CODER_QUERY_KEY_PREFIX` constant that you can use to group all Coder queries. `useCoderQuery` automatically injects this value into all its `queryKey` arrays. However, if you need to escape out with `useQuery`, you can import the constant and manually include it as the first value of your query key. | ||
|
||
In addition, all official Coder plugin components use this prefix internally. | ||
|
||
```tsx | ||
// All grouped queries can be invalidated at once from the query client | ||
const queryClient = useQueryClient(); | ||
const invalidateAllCoderQueries = () => { | ||
queryClient.invalidateQuery({ | ||
queryKey: [CODER_QUERY_KEY_PREFIX], | ||
}); | ||
}; | ||
|
||
// The prefix is only needed when NOT using useCoderQuery | ||
const customQuery = useQuery({ | ||
queryKey: [CODER_QUERY_KEY_PREFIX, 'workspaces'], | ||
queryFn: () => { | ||
// Your custom API logic | ||
}, | ||
}); | ||
|
||
// When the user unlinks their session token, all queries grouped under | ||
// CODER_QUERY_KEY_PREFIX are vacated from the active query cache | ||
function LogOutButton() { | ||
const { unlinkToken } = useCoderAuth(); | ||
|
||
return ( | ||
<button type="button" onClick={unlinkToken}> | ||
Unlink Coder account | ||
</button> | ||
); | ||
} | ||
``` | ||
|
||
## Recommendations for accessing the API | ||
|
||
1. If querying data, prefer `useCoderQuery`. It automatically wires up all auth logic to React Query (which includes pausing queries if the user is not authenticated). It also lets you access the Coder API via its query function. `useQuery` works as an escape hatch if `useCoderQuery` doesn't meet your needs, but it requires more work to wire up correctly. | ||
2. If mutating data, you will need to call `useMutation`, `useQueryClient`, and `useCoderApi` in tandem\*. | ||
|
||
We highly recommend **not** fetching with `useState` + `useEffect`, or with `useAsync`. Both face performance issues when trying to share state. See [ui.dev](https://www.ui.dev/)'s wonderful [_The Story of React Query_ video](https://www.youtube.com/watch?v=OrliU0e09io) for more info on some of the problems they face. | ||
|
||
\* `useCoderMutation` can be used instead of all three once that hook is available. | ||
|
||
### Comparing query caching strategies | ||
|
||
| | `useAsync` | `useQuery` | `useCoderQuery` | | ||
| ------------------------------------------------------------------ | ---------- | ---------- | --------------- | | ||
| Automatically handles race conditions | ✅ | ✅ | ✅ | | ||
| Can retain state after component unmounts | 🚫 | ✅ | ✅ | | ||
| Easy, on-command query invalidation | 🚫 | ✅ | ✅ | | ||
| Automatic retry logic when a query fails | 🚫 | ✅ | ✅ | | ||
| Less need to fight dependency arrays | 🚫 | ✅ | ✅ | | ||
| Easy to share state for sibling components | 🚫 | ✅ | ✅ | | ||
| Pre-wired to Coder auth logic | 🚫 | 🚫 | ✅ | | ||
| Can consume Coder API directly from query function | 🚫 | 🚫 | ✅ | | ||
| Automatically groups Coder-related queries by prefixing query keys | 🚫 | 🚫 | ✅ | | ||
|
||
## Authentication | ||
|
||
All API calls to **any** of the Coder API functions will fail if you have not authenticated yet. Authentication can be handled via any of the official Coder components that can be imported via the plugin. However, if there are no Coder components on the screen, the `CoderProvider` component will automatically\* inject a fallback auth button for letting the user add their auth info. | ||
|
||
https://github.com/coder/backstage-plugins/assets/28937484/0ece4410-36fc-4b32-9223-66f35953eeab | ||
|
||
Once the user has been authenticated, all Coder API functions will become available. When the user unlinks their auth token (effectively logging out), all cached queries that start with `CODER_QUERY_KEY_PREFIX` will automatically be vacated. | ||
|
||
\* This behavior can be disabled. Please see our [advanced API guide](./coder-api-advanced.md) for more information. | ||
|
||
## Component examples | ||
|
||
Here are some full code examples showcasing patterns you can bring into your own codebase. | ||
|
||
Note: To keep the examples simple, none of them contain any CSS styling or MUI components. | ||
|
||
### Displaying recent audit logs | ||
|
||
```tsx | ||
import React from 'react'; | ||
import { useCoderQuery } from '@coder/backstage-plugin-coder'; | ||
|
||
function RecentAuditLogsList() { | ||
const auditLogsQuery = useCoderQuery({ | ||
queryKey: ['audits', 'logs'], | ||
queryFn: ({ coderApi }) => coderApi.getAuditLogs({ limit: 10 }), | ||
}); | ||
|
||
return ( | ||
<> | ||
{auditLogsQuery.isLoading && <p>Loading…</p>} | ||
{auditLogsQuery.error instanceof Error && ( | ||
<p>Encountered the following error: {auditLogsQuery.error.message}</p> | ||
)} | ||
|
||
{auditLogsQuery.data !== undefined && ( | ||
<ul> | ||
{auditLogsQuery.data.audit_logs.map(log => ( | ||
<li key={log.id}>{log.description}</li> | ||
))} | ||
</ul> | ||
)} | ||
</> | ||
); | ||
} | ||
``` | ||
|
||
## Creating a new workspace | ||
|
||
Note: this example showcases how to perform mutations with `useMutation`. The example will be updated once `useCoderMutation` is available. | ||
|
||
```tsx | ||
import React, { type FormEvent, useState } from 'react'; | ||
import { useMutation, useQueryClient } from '@tanstack/react-query'; | ||
import { | ||
type CreateWorkspaceRequest, | ||
CODER_QUERY_KEY_PREFIX, | ||
useCoderQuery, | ||
useCoderApi, | ||
} from '@coder/backstage-plugin-coder'; | ||
|
||
export function WorkspaceCreationForm() { | ||
const [newWorkspaceName, setNewWorkspaceName] = useState(''); | ||
const coderApi = useCoderSdk(); | ||
const queryClient = useQueryClient(); | ||
|
||
const currentUserQuery = useCoderQuery({ | ||
queryKey: ['currentUser'], | ||
queryFn: coderApi.getAuthenticatedUser, | ||
}); | ||
|
||
const workspacesQuery = useCoderQuery({ | ||
queryKey: ['workspaces'], | ||
queryFn: coderApi.getWorkspaces, | ||
}); | ||
|
||
const createWorkspaceMutation = useMutation({ | ||
mutationFn: (payload: CreateWorkspaceRequest) => { | ||
if (currentUserQuery.data === undefined) { | ||
throw new Error( | ||
'Cannot create workspace without data for current user', | ||
); | ||
} | ||
|
||
const { organization_ids, id: userId } = currentUserQuery.data; | ||
return coderApi.createWorkspace(organization_ids[0], userId, payload); | ||
}, | ||
}); | ||
|
||
const onSubmit = async (event: FormEvent<HTMLFormElement>) => { | ||
event.preventDefault(); | ||
|
||
// If the mutation fails, useMutation will expose the error in the UI via | ||
// its own exposed properties | ||
await createWorkspaceMutation.mutateAsync({ | ||
name: newWorkspaceName, | ||
}); | ||
|
||
setNewWorkspaceName(''); | ||
queryClient.invalidateQueries({ | ||
queryKey: [CODER_QUERY_KEY_PREFIX, 'workspaces'], | ||
}); | ||
}; | ||
|
||
return ( | ||
<> | ||
{createWorkspaceMutation.isSuccess && ( | ||
<p> | ||
Workspace {createWorkspaceMutation.data.name} created successfully! | ||
</p> | ||
)} | ||
|
||
<form onSubmit={onSubmit}> | ||
<fieldset> | ||
<legend>Required fields</legend> | ||
|
||
<label> | ||
Workspace name | ||
<input | ||
type="text" | ||
value={newWorkspaceName} | ||
onChange={event => setNewWorkspaceName(event.target.value)} | ||
/> | ||
</label> | ||
</fieldset> | ||
|
||
<button type="submit">Create workspace</button> | ||
</form> | ||
</> | ||
); | ||
} | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.