Skip to content
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

feat: [M3-7028] - Add AGLB Edit Route Drawer #9822

Merged
merged 16 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add AGLB Edit Route Drawer ([#9822](https://github.com/linode/manager/pull/9822))
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,64 @@ import {
} from 'support/intercepts/load-balancers';

describe('Akamai Global Load Balancer routes page', () => {
it('can edit a route label and protocol', () => {
const indexOfRuleToEdit = 1;
const loadbalancer = loadbalancerFactory.build();
const routes = routeFactory.buildList(indexOfRuleToEdit, {
protocol: 'http',
});

mockAppendFeatureFlags({
aglb: makeFeatureFlagData(true),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientStream');
mockGetLoadBalancer(loadbalancer).as('getLoadBalancer');
mockGetLoadBalancerRoutes(loadbalancer.id, routes).as('getRoutes');

cy.visitWithLogin(`/loadbalancers/${loadbalancer.id}/routes`);
cy.wait([
'@getFeatureFlags',
'@getClientStream',
'@getLoadBalancer',
'@getRoutes',
]);

ui.actionMenu
.findByTitle(`Action Menu for Route ${routes[0].label}`)
.click();

ui.actionMenuItem.findByTitle('Edit').click();

mockUpdateRoute(loadbalancer, routes[0]).as('updateRoute');

ui.drawer
.findByTitle(`Edit Route: ${routes[0].label}`)
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
.should('be.visible')
.within(() => {
cy.findByLabelText('Route Label').should(
'have.value',
`${routes[0].label}`
);
// .clear() // TODO: [M3-7028] - Enable when we have real data and not mocks
// .type('new-label'); // TODO: [M3-7028] - Enable when we have real data and not mocks

cy.get('[data-qa-radio="HTTP"]').find('input').should('be.checked');
cy.get('[data-qa-radio="TCP"]').click();

ui.buttonGroup
.findButtonByTitle('Edit Route')
.should('be.visible')
.should('be.enabled');
// .click(); // TODO: [M3-7028] - Enable when we have real data and not mocks
});

// TODO: [M3-7028] - Enable when we have real data and not mocks
// cy.wait('@updateRoute');
// cy.findByLabelText('Route 0').within(() => {
// cy.findByText('new-label');
// cy.findByText('TCP');
// });
});
it('can add a HTTP rule', () => {
const loadbalancer = loadbalancerFactory.build();
const routes = routeFactory.buildList(1, { protocol: 'http' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useLoadBalancerRoutesQuery } from 'src/queries/aglb/routes';
import { CreateRouteDrawer } from './Routes/CreateRouteDrawer';
import { DeleteRouteDialog } from './Routes/DeleteRouteDialog';
import { DeleteRuleDialog } from './Routes/DeleteRuleDialog';
import { EditRouteDrawer } from './Routes/EditRouteDrawer';
import { RuleDrawer } from './Routes/RuleDrawer';
import { RulesTable } from './RulesTable';

Expand All @@ -37,6 +38,7 @@ const PREFERENCE_KEY = 'loadbalancer-routes';
export const LoadBalancerRoutes = () => {
const { loadbalancerId } = useParams<{ loadbalancerId: string }>();
const [isCreateDrawerOpen, setIsCreateDrawerOpen] = useState(false);
const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false);
const [isAddRuleDrawerOpen, setIsAddRuleDrawerOpen] = useState(false);
const [query, setQuery] = useState<string>();
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
Expand Down Expand Up @@ -94,6 +96,11 @@ export const LoadBalancerRoutes = () => {
setSelectedRuleIndex(ruleIndex);
};

const onEditRoute = (route: Route) => {
setIsEditDrawerOpen(true);
setSelectedRouteId(route.id);
};

const onDeleteRoute = (route: Route) => {
setIsDeleteDialogOpen(true);
setSelectedRouteId(route.id);
Expand All @@ -111,7 +118,7 @@ export const LoadBalancerRoutes = () => {
const OuterTableCells = (
<>
<Hidden smDown>
<TableCell>{route.rules.length}</TableCell>
<TableCell>{route.rules?.length}</TableCell>
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
</Hidden>
<Hidden smDown>
<TableCell>{route.protocol.toLocaleUpperCase()}</TableCell>{' '}
Expand All @@ -129,7 +136,7 @@ export const LoadBalancerRoutes = () => {
*/}
<ActionMenu
actionsList={[
{ onClick: () => null, title: 'Edit' },
{ onClick: () => onEditRoute(route), title: 'Edit' },
{ onClick: () => null, title: 'Clone Route' },
{ onClick: () => onDeleteRoute(route), title: 'Delete' },
]}
Expand Down Expand Up @@ -252,6 +259,12 @@ export const LoadBalancerRoutes = () => {
route={selectedRoute}
ruleIndexToEdit={selectedRuleIndex}
/>
<EditRouteDrawer
loadbalancerId={Number(loadbalancerId)}
onClose={() => setIsEditDrawerOpen(false)}
open={isEditDrawerOpen}
route={selectedRoute}
/>
<CreateRouteDrawer
loadbalancerId={Number(loadbalancerId)}
onClose={() => setIsCreateDrawerOpen(false)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { UpdateRoutePayload } from '@linode/api-v4';
import { useFormik } from 'formik';
import React from 'react';

import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { Drawer } from 'src/components/Drawer';
import { FormControlLabel } from 'src/components/FormControlLabel';
import { FormLabel } from 'src/components/FormLabel';
import { Notice } from 'src/components/Notice/Notice';
import { Radio } from 'src/components/Radio/Radio';
import { RadioGroup } from 'src/components/RadioGroup';
import { TextField } from 'src/components/TextField';
import { useLoadBalancerRouteUpdateMutation } from 'src/queries/aglb/routes';

import type { Route } from '@linode/api-v4';

interface Props {
loadbalancerId: number;
onClose: () => void;
open: boolean;
route: Route | undefined;
}

export const EditRouteDrawer = (props: Props) => {
const { loadbalancerId, onClose: _onClose, open, route } = props;

const {
error,
mutateAsync: updateRoute,
reset,
} = useLoadBalancerRouteUpdateMutation(loadbalancerId, route?.id ?? -1);

const formik = useFormik<UpdateRoutePayload>({
enableReinitialize: true,
initialValues: {
label: route?.label,
protocol: route?.protocol,
rules: route?.rules,
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
},
async onSubmit(values) {
await updateRoute(values);
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
onClose();
},
});

const onClose = () => {
formik.resetForm();
reset();
_onClose();
};

const generalError = error?.find((e) => !e.field)?.reason;

return (
<Drawer onClose={onClose} open={open} title={`Edit Route: ${route?.label}`}>
<form onSubmit={formik.handleSubmit}>
{generalError && <Notice text={generalError} variant="error" />}
<TextField
label="Route Label"
name="label"
onChange={formik.handleChange}
value={formik.values.label}
/>
bnussman-akamai marked this conversation as resolved.
Show resolved Hide resolved
<RadioGroup
onChange={(_, value) => formik.setFieldValue('protocol', value)}
value={formik.values.protocol}
>
<FormLabel sx={(theme) => ({ marginTop: theme.spacing(1) })}>
Protocol
</FormLabel>
<FormControlLabel
control={<Radio />}
data-qa-radio="HTTP"
label="HTTP"
value="http"
/>
<FormControlLabel
control={<Radio />}
data-qa-radio="TCP"
label="TCP"
value="tcp"
/>
</RadioGroup>
<ActionsPanel
primaryButtonProps={{
label: 'Edit Route',
loading: formik.isSubmitting,
jaalah-akamai marked this conversation as resolved.
Show resolved Hide resolved
type: 'submit',
}}
secondaryButtonProps={{
label: 'Cancel',
onClick: onClose,
}}
/>
</form>
</Drawer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const RuleDrawer = (props: Props) => {
ruleIndexToEdit,
} = props;

const ruleIndex = ruleIndexToEdit ?? route?.rules.length ?? 0;
const ruleIndex = ruleIndexToEdit ?? route?.rules?.length ?? 0;
bnussman-akamai marked this conversation as resolved.
Show resolved Hide resolved

const isEditMode = ruleIndexToEdit !== undefined;

Expand Down