Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d4e9b4a
organization list v1
david-crespo Feb 17, 2023
a861326
project list v1
david-crespo Feb 17, 2023
4cde77b
org view v1
david-crespo Feb 18, 2023
accdd05
project view v1, sorely in need of some helpers
david-crespo Feb 18, 2023
644fbbe
try it with more flexible looker uppers
david-crespo Feb 18, 2023
aceba2c
instance view and instance nics. call sites very verbose but MSW is c…
david-crespo Feb 18, 2023
ed5da05
update and delete nic
david-crespo Feb 18, 2023
4c5be03
missed a couple of instanceView calls
david-crespo Feb 18, 2023
3bd7837
notes on helpers, clean up lookups
david-crespo Feb 18, 2023
da4a34f
project create v1
david-crespo Feb 18, 2023
091d44d
fix object already exists error message for v1 creates, hook test
david-crespo Feb 18, 2023
316d263
project update v1
david-crespo Feb 18, 2023
31a0700
project delete v1
david-crespo Feb 18, 2023
05db7ac
attempt at "helpers" that are supposed to "help" but are actually "shit"
david-crespo Feb 20, 2023
b86700b
instance disk attach/detach, wrap of instance disk list
david-crespo Feb 20, 2023
125ebef
helpers actually save a few lines?!?!?!?!?!
david-crespo Feb 20, 2023
2142354
nuke all the legacy *ById endpoints in MSW
david-crespo Feb 20, 2023
02203a3
disk endpoints
david-crespo Feb 20, 2023
efce7cf
org create and fix instance create
david-crespo Feb 20, 2023
9f9918c
go around eliminating use of toApiSelector in the wild
david-crespo Feb 21, 2023
3ea9969
instances list, no more uses of useProjectParams
david-crespo Feb 21, 2023
2eeb794
vpc endpoints
david-crespo Feb 21, 2023
1024c0b
network interface create
david-crespo Feb 21, 2023
9f97277
vpc subnets
david-crespo Feb 21, 2023
ff5ab79
convert instance actions and delete some unused looker-uppers
david-crespo Feb 21, 2023
92302a9
remaining org endpoints
david-crespo Feb 21, 2023
83360ef
silo and project policy
david-crespo Feb 21, 2023
d249daa
more cleanup
david-crespo Feb 21, 2023
77405fc
some easy ones
david-crespo Feb 21, 2023
655b9a9
vpc router
david-crespo Feb 21, 2023
4bd8f47
apparently I shouldn't import things from @oxide/api to @oxide/api-mocks
david-crespo Feb 21, 2023
ca3bf7e
fix org access bug
david-crespo Feb 21, 2023
1428e65
sleds and physical disks and stuff
david-crespo Feb 21, 2023
47e6a60
router routes and some cleanup
david-crespo Feb 21, 2023
1750083
convert remaining pb to pb2
david-crespo Feb 21, 2023
bf4d3fd
rename pb2 back to pb
david-crespo Feb 21, 2023
18e0083
more cleanup
david-crespo Feb 21, 2023
3f5fbd3
combine path params back into one file
david-crespo Feb 21, 2023
ee77cbb
don't be silly
david-crespo Feb 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions app/components/TopBarPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Link, useParams } from 'react-router-dom'
import { useApiQuery } from '@oxide/api'
import { Identicon, Organization16Icon, SelectArrows6Icon, Success12Icon } from '@oxide/ui'

import { useInstanceParams, useProjectParams, useSiloParams } from 'app/hooks'
import { useInstanceSelector, useProjectSelector, useSiloParams } from 'app/hooks'
import { pb } from 'app/util/path-builder'

type TopBarPickerItem = {
Expand Down Expand Up @@ -122,7 +122,7 @@ export function useSiloSystemPicker(value: 'silo' | 'system') {
// this request in the loader. If that prefetch were removed, fleet viewers
// would see the silo picker pop in when the request resolves, which would be
// bad.
const { data: systemPolicy } = useApiQuery('systemPolicyView', {})
const { data: systemPolicy } = useApiQuery('systemPolicyViewV1', {})
return systemPolicy ? <SiloSystemPicker value={value} /> : null
}

Expand Down Expand Up @@ -157,7 +157,7 @@ export function SiloPicker() {
const { data } = useApiQuery('siloList', { query: { limit: 10 } })
const items = (data?.items || []).map((silo) => ({
label: silo.name,
to: pb.silo({ siloName: silo.name }),
to: pb.silo({ silo: silo.name }),
}))

return (
Expand All @@ -174,10 +174,10 @@ export function SiloPicker() {

export function OrgPicker() {
const { orgName } = useParams()
const { data } = useApiQuery('organizationList', { query: { limit: 20 } })
const { data } = useApiQuery('organizationListV1', { query: { limit: 20 } })
const items = (data?.items || []).map(({ name }) => ({
label: name,
to: pb.projects({ orgName: name }),
to: pb.projects({ organization: name }),
}))

return (
Expand All @@ -194,18 +194,20 @@ export function OrgPicker() {

export function ProjectPicker() {
// picker only shows up when a project is in scope
const { orgName, projectName } = useProjectParams()
const { data } = useApiQuery('projectList', { path: { orgName }, query: { limit: 20 } })
const { organization, project } = useProjectSelector()
const { data } = useApiQuery('projectListV1', {
query: { organization, limit: 20 },
})
const items = (data?.items || []).map(({ name }) => ({
label: name,
to: pb.instances({ orgName, projectName: name }),
to: pb.instances({ organization, project: name }),
}))

return (
<TopBarPicker
aria-label="Switch project"
category="Project"
current={projectName}
current={project}
items={items}
noItemsText="No projects found"
/>
Expand All @@ -214,21 +216,20 @@ export function ProjectPicker() {

export function InstancePicker() {
// picker only shows up when an instance is in scope
const { orgName, projectName, instanceName } = useInstanceParams()
const { data } = useApiQuery('instanceList', {
path: { orgName, projectName },
query: { limit: 50 },
const { organization, project, instance } = useInstanceSelector()
const { data } = useApiQuery('instanceListV1', {
query: { organization, project, limit: 50 },
})
const items = (data?.items || []).map(({ name }) => ({
label: name,
to: pb.instance({ orgName, projectName, instanceName: name }),
to: pb.instance({ organization, project, instance: name }),
}))

return (
<TopBarPicker
aria-label="Switch instance"
category="Instance"
current={instanceName}
current={instance}
items={items}
noItemsText="No instances found"
/>
Expand Down
8 changes: 4 additions & 4 deletions app/components/form/fields/SubnetListbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useWatch } from 'react-hook-form'

import { useApiQuery } from '@oxide/api'

import { useRequiredParams } from 'app/hooks'
import { useProjectSelector } from 'app/hooks'

import type { ListboxFieldProps } from './ListboxField'
import { ListboxField } from './ListboxField'
Expand All @@ -26,7 +26,7 @@ export function SubnetListbox<
TFieldValues extends FieldValues,
TName extends FieldPath<TFieldValues>
>({ vpcNameField, control, ...fieldProps }: SubnetListboxProps<TFieldValues, TName>) {
const pathParams = useRequiredParams('orgName', 'projectName')
const projectSelector = useProjectSelector()

const [vpcName] = useWatch({ control, name: [vpcNameField] })

Expand All @@ -36,8 +36,8 @@ export function SubnetListbox<
// TODO: error handling other than fallback to empty list?
const subnets =
useApiQuery(
'vpcSubnetList',
{ path: { ...pathParams, vpcName } },
'vpcSubnetListV1',
{ query: { ...projectSelector, vpc: vpcName } },
{
enabled: vpcExists,
useErrorBoundary: false,
Expand Down
23 changes: 15 additions & 8 deletions app/forms/disk-attach.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useParams } from 'react-router-dom'
import invariant from 'tiny-invariant'

import type { Disk, DiskIdentifier } from '@oxide/api'
import { useApiMutation, useApiQuery, useApiQueryClient } from '@oxide/api'

import { ListboxField, SideModalForm } from 'app/components/form'
import { useAllParams } from 'app/hooks'
import { useProjectSelector } from 'app/hooks'

const defaultValues = { name: '' }

Expand All @@ -21,13 +22,18 @@ export function AttachDiskSideModalForm({
onDismiss,
}: AttachDiskProps) {
const queryClient = useApiQueryClient()
const { orgName, projectName, instanceName } = useAllParams('orgName', 'projectName')
// instance name undefined when this form is called from DisksTableField on
// instance create, which passes in its own onSubmit, bypassing the attachDisk mutation
const { instanceName } = useParams()
const projectSelector = useProjectSelector()

const attachDisk = useApiMutation('instanceDiskAttach', {
// TODO: pass in this mutation from outside so we don't have to do the instanceName check
const attachDisk = useApiMutation('instanceDiskAttachV1', {
onSuccess(data) {
invariant(instanceName, 'instanceName is required')
queryClient.invalidateQueries('instanceDiskList', {
path: { orgName, projectName, instanceName },
queryClient.invalidateQueries('instanceDiskListV1', {
path: { instance: instanceName },
query: projectSelector,
})
onSuccess?.(data)
onDismiss()
Expand All @@ -39,7 +45,7 @@ export function AttachDiskSideModalForm({
// click in
// TODO: error handling
const detachedDisks =
useApiQuery('diskList', { path: { orgName, projectName } }).data?.items.filter(
useApiQuery('diskListV1', { query: projectSelector }).data?.items.filter(
(d) => d.state.state === 'detached'
) || []

Expand All @@ -53,8 +59,9 @@ export function AttachDiskSideModalForm({
(({ name }) => {
invariant(instanceName, 'instanceName is required')
attachDisk.mutate({
path: { orgName, projectName, instanceName },
body: { name },
path: { instance: instanceName },
query: projectSelector,
body: { disk: name },
})
})
}
Expand Down
10 changes: 5 additions & 5 deletions app/forms/disk-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
RadioField,
SideModalForm,
} from 'app/components/form'
import { useRequiredParams, useToast } from 'app/hooks'
import { useProjectSelector, useToast } from 'app/hooks'

const defaultValues: DiskCreate = {
name: '',
Expand Down Expand Up @@ -46,13 +46,13 @@ export function CreateDiskSideModalForm({
onDismiss,
}: CreateSideModalFormProps) {
const queryClient = useApiQueryClient()
const pathParams = useRequiredParams('orgName', 'projectName')
const projectSelector = useProjectSelector()
Copy link
Contributor

Choose a reason for hiding this comment

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

I do like this API overall.

const addToast = useToast()
const navigate = useNavigate()

const createDisk = useApiMutation('diskCreate', {
const createDisk = useApiMutation('diskCreateV1', {
onSuccess(data) {
queryClient.invalidateQueries('diskList', { path: pathParams })
queryClient.invalidateQueries('diskListV1', { query: projectSelector })
addToast({
icon: <Success16Icon />,
title: 'Success!',
Expand All @@ -71,7 +71,7 @@ export function CreateDiskSideModalForm({
onDismiss={() => onDismiss(navigate)}
onSubmit={({ size, ...rest }) => {
const body = { size: size * GiB, ...rest }
onSubmit ? onSubmit(body) : createDisk.mutate({ path: pathParams, body })
onSubmit ? onSubmit(body) : createDisk.mutate({ query: projectSelector, body })
}}
loading={createDisk.isLoading}
submitError={createDisk.error}
Expand Down
2 changes: 1 addition & 1 deletion app/forms/idp-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function CreateIdpSideModalForm() {

const { siloName } = useSiloParams()

const onDismiss = () => navigate(pb.silo({ siloName }))
const onDismiss = () => navigate(pb.silo({ silo: siloName }))

const createIdp = useApiMutation('samlIdentityProviderCreate', {
onSuccess() {
Expand Down
31 changes: 17 additions & 14 deletions app/forms/instance-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import invariant from 'tiny-invariant'
import type { SetRequired } from 'type-fest'

import type { InstanceCreate } from '@oxide/api'
import { apiQueryClient } from '@oxide/api'
import { genName } from '@oxide/api'
import { useApiQuery } from '@oxide/api'
import { useApiMutation, useApiQueryClient } from '@oxide/api'
import {
apiQueryClient,
genName,
useApiMutation,
useApiQuery,
useApiQueryClient,
} from '@oxide/api'
import {
Divider,
Instances24Icon,
Expand All @@ -31,7 +34,7 @@ import {
RadioFieldDyn,
TextField,
} from 'app/components/form'
import { useRequiredParams, useToast } from 'app/hooks'
import { useProjectSelector, useToast } from 'app/hooks'
import { pb } from 'app/util/path-builder'

export type InstanceCreateInput = Assign<
Expand Down Expand Up @@ -76,25 +79,25 @@ CreateInstanceForm.loader = async () => {
export function CreateInstanceForm() {
const queryClient = useApiQueryClient()
const addToast = useToast()
const pageParams = useRequiredParams('orgName', 'projectName')
const projectSelector = useProjectSelector()
const navigate = useNavigate()

const createInstance = useApiMutation('instanceCreate', {
const createInstance = useApiMutation('instanceCreateV1', {
onSuccess(instance) {
// refetch list of instances
queryClient.invalidateQueries('instanceList', { path: pageParams })
queryClient.invalidateQueries('instanceListV1', { query: projectSelector })
// avoid the instance fetch when the instance page loads since we have the data
queryClient.setQueryData(
'instanceView',
{ path: { ...pageParams, instanceName: instance.name } },
'instanceViewV1',
{ path: { instance: instance.name }, query: projectSelector },
instance
)
addToast({
icon: <Success16Icon />,
title: 'Success!',
content: 'Your instance has been created.',
})
navigate(pb.instancePage({ ...pageParams, instanceName: instance.name }))
navigate(pb.instancePage({ ...projectSelector, instance: instance.name }))
},
})

Expand Down Expand Up @@ -128,11 +131,11 @@ export function CreateInstanceForm() {
const bootDiskName = values.bootDiskName || genName(values.name, image.name)

createInstance.mutate({
path: pageParams,
query: projectSelector,
body: {
name: values.name,
hostname: values.hostname || values.name,
description: `An instance in project: ${pageParams.projectName}`,
description: `An instance in project: ${projectSelector.project}`,
memory: instance.memory * GiB,
ncpus: instance.ncpus,
disks: [
Expand Down Expand Up @@ -263,7 +266,7 @@ export function CreateInstanceForm() {

<Form.Actions>
<Form.Submit loading={createInstance.isLoading}>Create instance</Form.Submit>
<Form.Cancel onClick={() => navigate(pb.instances(pageParams))} />
<Form.Cancel onClick={() => navigate(pb.instances(projectSelector))} />
</Form.Actions>
</>
)}
Expand Down
20 changes: 11 additions & 9 deletions app/forms/network-interface-create.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useMemo } from 'react'
import { useParams } from 'react-router-dom'
import invariant from 'tiny-invariant'

import type { NetworkInterfaceCreate } from '@oxide/api'
import { useApiQuery } from '@oxide/api'
import { useApiMutation, useApiQueryClient } from '@oxide/api'
import { useApiMutation, useApiQuery, useApiQueryClient } from '@oxide/api'
import { Divider } from '@oxide/ui'

import {
Expand All @@ -14,7 +14,7 @@ import {
SubnetListbox,
TextField,
} from 'app/components/form'
import { useAllParams } from 'app/hooks'
import { useProjectSelector } from 'app/hooks'

const defaultValues: NetworkInterfaceCreate = {
name: '',
Expand All @@ -34,19 +34,21 @@ export default function CreateNetworkInterfaceForm({
onDismiss,
}: CreateNetworkInterfaceFormProps) {
const queryClient = useApiQueryClient()
const { orgName, projectName, instanceName } = useAllParams('orgName', 'projectName')
const { instanceName } = useParams()
const projectSelector = useProjectSelector()

const createNetworkInterface = useApiMutation('instanceNetworkInterfaceCreate', {
// TODO: pass in this mutation from outside so we don't have to do the instanceName check
const createNetworkInterface = useApiMutation('instanceNetworkInterfaceCreateV1', {
onSuccess() {
invariant(instanceName, 'instanceName is required when posting a network interface')
queryClient.invalidateQueries('instanceNetworkInterfaceList', {
path: { instanceName, projectName, orgName },
queryClient.invalidateQueries('instanceNetworkInterfaceListV1', {
query: { instance: instanceName, ...projectSelector },
})
onDismiss()
},
})

const { data: vpcsData } = useApiQuery('vpcList', { path: { orgName, projectName } })
const { data: vpcsData } = useApiQuery('vpcListV1', { query: projectSelector })
const vpcs = useMemo(() => vpcsData?.items || [], [vpcsData])

return (
Expand All @@ -64,7 +66,7 @@ export default function CreateNetworkInterfaceForm({
)

createNetworkInterface.mutate({
path: { instanceName, projectName, orgName },
query: { ...projectSelector, instance: instanceName },
body,
})
})
Expand Down
Loading