Skip to content

Commit

Permalink
feat(renterd): contracts batch manage blocklist and allowlist
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Nov 22, 2024
1 parent b37a029 commit e7a1ada
Show file tree
Hide file tree
Showing 20 changed files with 387 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .changeset/shiny-gifts-hope.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
'renterd': minor
---

The contracts multi-select menu now supports batch deletion.
The contracts multi-select menu now supports bulk deletion.
5 changes: 5 additions & 0 deletions .changeset/shy-dingos-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'renterd': minor
---

The contracts multi-select menu now supports bulk adding and removing to both the allowlist and blocklists.
20 changes: 19 additions & 1 deletion apps/renterd-e2e/src/fixtures/hosts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Locator, Page, expect } from '@playwright/test'
import { maybeExpectAndReturn, step } from '@siafoundation/e2e'
import {
fillTextInputByName,
maybeExpectAndReturn,
openCmdkMenu,
step,
} from '@siafoundation/e2e'

export const getHostRowById = step(
'get host row by ID',
Expand Down Expand Up @@ -57,3 +62,16 @@ export const openRowHostContextMenu = step(
return menu.click()
}
)

export const openManageListsDialog = step(
'open manage lists dialog',
async (page: Page) => {
const dialog = await openCmdkMenu(page)
await fillTextInputByName(page, 'cmdk-input', 'manage filter lists')
await expect(dialog.locator('div[cmdk-item]')).toHaveCount(1)
await dialog
.locator('div[cmdk-item]')
.getByText('manage filter lists')
.click()
}
)
75 changes: 74 additions & 1 deletion apps/renterd-e2e/src/specs/contracts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
getContractRows,
getContractsSummaryRow,
} from '../fixtures/contracts'
import { openManageListsDialog } from '../fixtures/hosts'

test.beforeEach(async ({ page }) => {
await beforeTest(page, {
Expand Down Expand Up @@ -62,7 +63,7 @@ test('contracts prunable size', async ({ page }) => {
}
})

test('batch delete contracts', async ({ page }) => {
test('contracts bulk delete', async ({ page }) => {
await navigateToContracts({ page })
const rows = await getContractRows(page)
for (const row of rows) {
Expand All @@ -79,3 +80,75 @@ test('batch delete contracts', async ({ page }) => {
page.getByText('There are currently no active contracts')
).toBeVisible()
})

test('contracts bulk allowlist', async ({ page }) => {
await navigateToContracts({ page })
const rows = await getContractRows(page)
for (const row of rows) {
await row.click()
}

const menu = page.getByLabel('contract multi-select menu')
const dialog = page.getByRole('dialog')

// Add selected contract hosts to the allowlist.
await menu.getByLabel('add host public keys to allowlist').click()
await dialog.getByRole('button', { name: 'Add to allowlist' }).click()

await openManageListsDialog(page)
await expect(dialog.getByText('The blocklist is empty')).toBeVisible()
await dialog.getByLabel('view allowlist').click()
await expect(
dialog.getByTestId('allowlistPublicKeys').getByTestId('item')
).toHaveCount(3)
await dialog.getByLabel('close').click()

for (const row of rows) {
await row.click()
}

// Remove selected contract hosts from the allowlist.
await menu.getByLabel('remove host public keys from allowlist').click()
await dialog.getByRole('button', { name: 'Remove from allowlist' }).click()

await openManageListsDialog(page)
await expect(dialog.getByText('The blocklist is empty')).toBeVisible()
await dialog.getByLabel('view allowlist').click()
await expect(dialog.getByText('The allowlist is empty')).toBeVisible()
})

test('contracts bulk blocklist', async ({ page }) => {
await navigateToContracts({ page })
const rows = await getContractRows(page)
for (const row of rows) {
await row.click()
}

const menu = page.getByLabel('contract multi-select menu')
const dialog = page.getByRole('dialog')

// Add selected contract hosts to the allowlist.
await menu.getByLabel('add host addresses to blocklist').click()
await dialog.getByRole('button', { name: 'Add to blocklist' }).click()

await openManageListsDialog(page)
await expect(
dialog.getByTestId('blocklistAddresses').getByTestId('item')
).toHaveCount(3)
await dialog.getByLabel('view allowlist').click()
await expect(dialog.getByText('The allowlist is empty')).toBeVisible()
await dialog.getByLabel('close').click()

for (const row of rows) {
await row.click()
}

// Remove selected contract hosts from the blocklist.
await menu.getByLabel('remove host addresses from blocklist').click()
await dialog.getByRole('button', { name: 'Remove from blocklist' }).click()

await openManageListsDialog(page)
await expect(dialog.getByText('The blocklist is empty')).toBeVisible()
await dialog.getByLabel('view allowlist').click()
await expect(dialog.getByText('The allowlist is empty')).toBeVisible()
})
4 changes: 2 additions & 2 deletions apps/renterd-e2e/src/specs/files.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ test('shows a new intermediate directory when uploading nested files', async ({
await deleteBucket(page, bucketName)
})

test('batch delete across nested directories', async ({ page }) => {
test('bulk delete across nested directories', async ({ page }) => {
const bucketName = 'bucket1'
await navigateToBuckets({ page })
await createBucket(page, bucketName)
Expand Down Expand Up @@ -267,7 +267,7 @@ test('batch delete across nested directories', async ({ page }) => {
})
})

test('batch delete using the all files explorer mode', async ({ page }) => {
test('bulk delete using the all files explorer mode', async ({ page }) => {
const bucketName = 'bucket1'
await navigateToBuckets({ page })
await createBucket(page, bucketName)
Expand Down
2 changes: 1 addition & 1 deletion apps/renterd-e2e/src/specs/filesMove.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ test('move a file via drag and drop while leaving a separate set of selected fil
})
})

test('move files by selecting and using the docked menu batch action', async ({
test('move files by selecting and using the docked menu bulk action', async ({
page,
}) => {
const bucketName = 'bucket1'
Expand Down
2 changes: 1 addition & 1 deletion apps/renterd-e2e/src/specs/keys.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test('create and delete a key', async ({ page }) => {
await expect(row).toBeHidden()
})

test('batch delete multiple keys', async ({ page }) => {
test('bulk delete multiple keys', async ({ page }) => {
// Create 3 keys. Note: 1 already exists.
const key1 = await createKey(page)
const key2 = await createKey(page)
Expand Down
6 changes: 3 additions & 3 deletions apps/renterd/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@

- 17b29cf3: Navigating into a directory in the file explorer is now by clicking on the directory name rather than anywhere on the row.
- 17b29cf3: The directory-based file explorer now supports multiselect across any files and directories.
- 6c7e3681: The key management table now supports multiselect and batch deletion.
- 6c7e3681: The key management table now supports multiselect and bulk deletion.
- 17b29cf3: The "all files" file explorer now supports multiselect across any files.
- 17b29cf3: The "all files" file explorer multiselect menu now supports batch deletion of selected files.
- 17b29cf3: The "all files" file explorer multiselect menu now supports bulk deletion of selected files.
- 6c7e3681: The onboarding wizard now animates in and out.
- ed264a0d: The transfers bar now animates in and out.
- 09142864: The keys table now has pagination controls.
- 17b29cf3: The directory-based file explorer multiselect menu now supports batch deletion of selected files and directories.
- 17b29cf3: The directory-based file explorer multiselect menu now supports bulk deletion of selected files and directories.

### Patch Changes

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Button, Paragraph } from '@siafoundation/design-system'
import { ListChecked16 } from '@siafoundation/react-icons'
import { useCallback, useMemo } from 'react'
import { useDialog } from '../../../contexts/dialog'
import { useContracts } from '../../../contexts/contracts'
import { pluralize } from '@siafoundation/units'
import { useAllowlistUpdate } from '../../../hooks/useAllowlistUpdate'

export function ContractsAddAllowlist() {
const { multiSelect } = useContracts()

const publicKeys = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => item.hostKey),
[multiSelect.selectionMap]
)
const { openConfirmDialog } = useDialog()
const allowlistUpdate = useAllowlistUpdate()

const add = useCallback(async () => {
allowlistUpdate(publicKeys, [])
multiSelect.deselectAll()
}, [allowlistUpdate, multiSelect, publicKeys])

return (
<Button
aria-label="add host public keys to allowlist"
tip="Add host public keys to allowlist"
onClick={() => {
openConfirmDialog({
title: `Add ${pluralize(
multiSelect.selectionCount,
'host'
)} to allowlist`,
action: 'Add to allowlist',
variant: 'accent',
body: (
<div className="flex flex-col gap-1">
<Paragraph size="14">
Are you sure you would like to add{' '}
{pluralize(multiSelect.selectionCount, 'host public key')} to
the allowlist?
</Paragraph>
</div>
),
onConfirm: add,
})
}}
>
<ListChecked16 />
Add to allowlist
</Button>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Button, Paragraph } from '@siafoundation/design-system'
import { ListChecked16 } from '@siafoundation/react-icons'
import { useCallback, useMemo } from 'react'
import { useDialog } from '../../../contexts/dialog'
import { useContracts } from '../../../contexts/contracts'
import { pluralize } from '@siafoundation/units'
import { useBlocklistUpdate } from '../../../hooks/useBlocklistUpdate'

export function ContractsAddBlocklist() {
const { multiSelect } = useContracts()

const hostAddresses = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => item.hostIp),
[multiSelect.selectionMap]
)
const { openConfirmDialog } = useDialog()
const blocklistUpdate = useBlocklistUpdate()

const add = useCallback(async () => {
blocklistUpdate(hostAddresses, [])
multiSelect.deselectAll()
}, [blocklistUpdate, multiSelect, hostAddresses])

return (
<Button
aria-label="add host addresses to blocklist"
tip="Add host addresses to blocklist"
onClick={() => {
openConfirmDialog({
title: `Add ${pluralize(
multiSelect.selectionCount,
'host'
)} to blocklist`,
action: 'Add to blocklist',
variant: 'red',
body: (
<div className="flex flex-col gap-1">
<Paragraph size="14">
Are you sure you would like to add{' '}
{pluralize(
multiSelect.selectionCount,
'host address',
'host addresses'
)}{' '}
to the blocklist?
</Paragraph>
</div>
),
onConfirm: add,
})
}}
>
<ListChecked16 />
Add to blocklist
</Button>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Button, Paragraph } from '@siafoundation/design-system'
import { ListChecked16 } from '@siafoundation/react-icons'
import { useCallback, useMemo } from 'react'
import { useDialog } from '../../../contexts/dialog'
import { useContracts } from '../../../contexts/contracts'
import { pluralize } from '@siafoundation/units'
import { useAllowlistUpdate } from '../../../hooks/useAllowlistUpdate'

export function ContractsRemoveAllowlist() {
const { multiSelect } = useContracts()

const publicKeys = useMemo(
() =>
Object.entries(multiSelect.selectionMap).map(([_, item]) => item.hostKey),
[multiSelect.selectionMap]
)
const { openConfirmDialog } = useDialog()
const allowlistUpdate = useAllowlistUpdate()

const remove = useCallback(async () => {
await allowlistUpdate([], publicKeys)
multiSelect.deselectAll()
}, [allowlistUpdate, multiSelect, publicKeys])

return (
<Button
aria-label="remove host public keys from allowlist"
tip="Remove host public keys from allowlist"
onClick={() => {
openConfirmDialog({
title: `Remove ${pluralize(
multiSelect.selectionCount,
'host'
)} from allowlist`,
action: 'Remove from allowlist',
variant: 'accent',
body: (
<div className="flex flex-col gap-1">
<Paragraph size="14">
Are you sure you would like to remove{' '}
{pluralize(multiSelect.selectionCount, 'host public key')} from
the allowlist?
</Paragraph>
</div>
),
onConfirm: remove,
})
}}
>
<ListChecked16 />
Remove from allowlist
</Button>
)
}
Loading

0 comments on commit e7a1ada

Please sign in to comment.