Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
db8e524
Switch param to route tabs: VPC firewall rules and subnets
benjaminleonard May 17, 2024
d5ef769
Test fixes
benjaminleonard May 17, 2024
a5ddc63
Add missing paths to test
benjaminleonard May 17, 2024
45c7f41
Create firewall from template
benjaminleonard May 17, 2024
19c6644
Update path-builder.spec.ts
benjaminleonard May 17, 2024
428aedb
Add missing license
benjaminleonard May 17, 2024
ecd5967
Move `incrementName` into `str` and add test
benjaminleonard May 17, 2024
b291892
Update import
benjaminleonard May 17, 2024
1c2fd0c
refactor rule to form values logic
david-crespo May 23, 2024
aa31af4
basic test
david-crespo May 23, 2024
c07ff1a
Merge branch 'main' into vpc-routes
david-crespo May 23, 2024
42d7d50
Merge vpc-routes into firewall-template
david-crespo May 23, 2024
5ed3c68
clean up vpc and instance detail in path builder
david-crespo May 23, 2024
cfd21ba
Merge vpc-routes into firewall-template
david-crespo May 23, 2024
373feda
fix warning on firewall rules leaf route without element
david-crespo May 23, 2024
2ddf55d
clean up path params and path builder stuff a bit
david-crespo May 24, 2024
6fa5edd
Merge branch 'main' into vpc-routes
david-crespo Jun 7, 2024
24a9b56
merge vpc-routes into firewall-template
david-crespo Jun 7, 2024
e54cadb
Merge vpc-routes into firewall-template
david-crespo Jun 7, 2024
21b2a06
whoops
david-crespo Jun 7, 2024
1b15d70
Merge branch 'main' into firewall-template
david-crespo Jun 27, 2024
3a69317
Merge main into firewall-template
david-crespo Jun 27, 2024
ca5f05f
let's go with clone
david-crespo Jul 16, 2024
922da55
make the min-width for more actions menus smaller
david-crespo Jul 16, 2024
e10a8a2
remove special min width on top bar dropdown menu that wasn't working…
david-crespo Jul 16, 2024
21c3b2d
fix e2e test and assert more stuff about the form
david-crespo Jul 16, 2024
332b26e
be less smart about copied name, just add -copy
david-crespo Jul 16, 2024
f2f508b
rename path builder function
david-crespo Jul 16, 2024
e8de806
Merge branch 'main' into firewall-template
david-crespo Jul 16, 2024
18cc299
we're doing comments. we love comments
david-crespo Jul 16, 2024
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
6 changes: 1 addition & 5 deletions app/components/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,7 @@ export function TopBar({ children }: { children: React.ReactNode }) {
<DirectionDownIcon className="!w-2.5" />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content
align="end"
sideOffset={8}
className="min-w-[12.8125rem]"
>
<DropdownMenu.Content align="end" sideOffset={8}>
<DropdownMenu.Item onSelect={() => navigate(pb.profile())}>
Settings
</DropdownMenu.Item>
Expand Down
26 changes: 24 additions & 2 deletions app/forms/firewall-rules-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import { useMemo } from 'react'
import { useController, type Control } from 'react-hook-form'
import { useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
import { useNavigate, useParams, type LoaderFunctionArgs } from 'react-router-dom'
import * as R from 'remeda'

import {
Expand Down Expand Up @@ -74,7 +74,17 @@ export const valuesToRuleUpdate = (values: FirewallRuleValues): VpcFirewallRuleU
targets: values.targets,
})

const defaultValues: FirewallRuleValues = {
/** convert in the opposite direction for when we're creating from existing rule */
const ruleToValues = (rule: VpcFirewallRule): FirewallRuleValues => ({
...rule,
enabled: rule.status === 'enabled',
protocols: rule.filters.protocols || [],
ports: rule.filters.ports || [],
hosts: rule.filters.hosts || [],
})

/** Empty form for when we're not creating from an existing rule */
const defaultValuesEmpty: FirewallRuleValues = {
enabled: true,
name: '',
description: '',
Expand Down Expand Up @@ -586,6 +596,18 @@ export function CreateFirewallRuleForm() {
})
const existingRules = useMemo(() => R.sortBy(data.rules, (r) => r.priority), [data])

// The :rule path param is optional. If it is present, we are creating a
// rule from an existing one, so we find that rule and copy it into the form
// values. Note that if we fail to find the rule by name (which should be
// very unlikely) we just pretend we never saw a name in the path and start
// from scratch.
const { rule: ruleName } = useParams()
const originalRule = existingRules.find((rule) => rule.name === ruleName)

const defaultValues: FirewallRuleValues = originalRule
? ruleToValues({ ...originalRule, name: originalRule.name + '-copy' })
: defaultValuesEmpty

const form = useForm({ defaultValues })

return (
Expand Down
6 changes: 6 additions & 0 deletions app/pages/project/vpcs/VpcPage/tabs/VpcFirewallRulesTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ export function VpcFirewallRulesTab() {
navigate(pb.vpcFirewallRuleEdit({ ...vpcSelector, rule: rule.name }))
},
},
{
label: 'Clone',
onActivate() {
navigate(pb.vpcFirewallRuleClone({ ...vpcSelector, rule: rule.name }))
},
},
{
label: 'Delete',
onActivate: confirmDelete({
Expand Down
2 changes: 0 additions & 2 deletions app/pages/project/vpcs/VpcPage/tabs/VpcSubnetsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ export function VpcSubnetsTab() {
[vpcSelector, makeActions]
)

// const columns = useColsWithActions(staticCols, makeActions)

const emptyState = (
<EmptyMessage
title="No VPC subnets"
Expand Down
2 changes: 1 addition & 1 deletion app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ export const routes = createRoutesFromElements(
element={null}
/>
<Route
path="firewall-rules-new"
path="firewall-rules-new/:rule?"
element={<CreateFirewallRuleForm />}
loader={CreateFirewallRuleForm.loader}
handle={{ crumb: 'New Firewall Rule' }}
Expand Down
2 changes: 1 addition & 1 deletion app/ui/styles/components/menu-button.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

.DropdownMenuContent {
@apply z-30 min-w-[14rem] rounded border p-0 bg-raise border-secondary;
@apply z-30 min-w-36 rounded border p-0 bg-raise border-secondary;

& .DropdownMenuItem {
@apply block cursor-pointer select-none border-b py-2 pl-3 pr-6 text-left text-sans-md text-secondary border-secondary last-of-type:border-b-0;
Expand Down
1 change: 1 addition & 0 deletions app/util/path-builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ test('path builder', () => {
"systemUtilization": "/system/utilization",
"vpc": "/projects/p/vpcs/v/firewall-rules",
"vpcEdit": "/projects/p/vpcs/v/edit",
"vpcFirewallRuleClone": "/projects/p/vpcs/v/firewall-rules-new/fr",
"vpcFirewallRuleEdit": "/projects/p/vpcs/v/firewall-rules/fr/edit",
"vpcFirewallRules": "/projects/p/vpcs/v/firewall-rules",
"vpcFirewallRulesNew": "/projects/p/vpcs/v/firewall-rules-new",
Expand Down
3 changes: 2 additions & 1 deletion app/util/path-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ export const pb = {

// same deal as instance detail: go straight to first tab
vpc: (params: Vpc) => pb.vpcFirewallRules(params),

vpcEdit: (params: Vpc) => `${vpcBase(params)}/edit`,

vpcFirewallRules: (params: Vpc) => `${vpcBase(params)}/firewall-rules`,
vpcFirewallRulesNew: (params: Vpc) => `${vpcBase(params)}/firewall-rules-new`,
vpcFirewallRuleClone: (params: FirewallRule) =>
`${pb.vpcFirewallRulesNew(params)}/${params.rule}`,
vpcFirewallRuleEdit: (params: FirewallRule) =>
`${pb.vpcFirewallRules(params)}/${params.rule}/edit`,
vpcSubnets: (params: Vpc) => `${vpcBase(params)}/subnets`,
Expand Down
33 changes: 32 additions & 1 deletion test/e2e/firewall-rules.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Copyright Oxide Computer Company
*/

import { expect, expectRowVisible, test } from './utils'
import { clickRowAction, expect, expectRowVisible, test } from './utils'

const defaultRules = ['allow-internal-inbound', 'allow-ssh', 'allow-icmp', 'allow-rdp']

Expand Down Expand Up @@ -310,6 +310,37 @@ test('can update firewall rule', async ({ page }) => {
}
})

test('create from existing rule', async ({ page }) => {
const url = '/projects/mock-project/vpcs/mock-vpc/firewall-rules'
await page.goto(url)

const modal = page.getByRole('dialog', { name: 'Add firewall rule' })
await expect(modal).toBeHidden()

await clickRowAction(page, 'allow-rdp', 'Clone')

await expect(page).toHaveURL(url + '-new/allow-rdp')
await expect(modal).toBeVisible()
await expect(modal.getByRole('textbox', { name: 'Name', exact: true })).toHaveValue(
'allow-rdp-copy'
)

await expect(modal.getByRole('checkbox', { name: 'TCP' })).toBeChecked()
await expect(modal.getByRole('checkbox', { name: 'UDP' })).not.toBeChecked()
await expect(modal.getByRole('checkbox', { name: 'ICMP' })).not.toBeChecked()

await expect(
modal
.getByRole('table', { name: 'Port filters' })
.getByRole('cell', { name: '3389', exact: true })
).toBeVisible()
await expect(
modal
.getByRole('table', { name: 'Targets' })
.getByRole('row', { name: 'Name: default, Type: vpc' })
).toBeVisible()
})

const rulePath = '/projects/mock-project/vpcs/mock-vpc/firewall-rules/allow-icmp/edit'

test('can edit rule directly by URL', async ({ page }) => {
Expand Down