Skip to content

Commit 2b03ff4

Browse files
authored
upcoming: [DPS-34039] Add actions in Streams list (linode#12645)
1 parent a819d73 commit 2b03ff4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1418
-417
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/api-v4": Upcoming Features
3+
---
4+
5+
DELETE, PUT API endpoints for Streams ([#12645](https://github.com/linode/manager/pull/12645))

packages/api-v4/src/account/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ export const EventActionKeys = [
470470
'stackscript_revise',
471471
'stackscript_update',
472472
'stream_create',
473+
'stream_delete',
474+
'stream_update',
473475
'subnet_create',
474476
'subnet_delete',
475477
'subnet_update',

packages/api-v4/src/datastream/streams.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createStreamSchema } from '@linode/validation';
1+
import { createStreamSchema, updateStreamSchema } from '@linode/validation';
22

33
import { BETA_API_ROOT } from '../constants';
44
import Request, {
@@ -10,7 +10,7 @@ import Request, {
1010
} from '../request';
1111

1212
import type { Filter, ResourcePage as Page, Params } from '../types';
13-
import type { CreateStreamPayload, Stream } from './types';
13+
import type { CreateStreamPayload, Stream, UpdateStreamPayload } from './types';
1414

1515
/**
1616
* Returns all the information about a specified Stream.
@@ -47,3 +47,27 @@ export const createStream = (data: CreateStreamPayload) =>
4747
setURL(`${BETA_API_ROOT}/monitor/streams`),
4848
setMethod('POST'),
4949
);
50+
51+
/**
52+
* Updates a Stream.
53+
*
54+
* @param streamId { number } The ID of the Stream.
55+
* @param data { object } Options for type, status, etc.
56+
*/
57+
export const updateStream = (streamId: number, data: UpdateStreamPayload) =>
58+
Request<Stream>(
59+
setData(data, updateStreamSchema),
60+
setURL(`${BETA_API_ROOT}/monitor/streams/${encodeURIComponent(streamId)}`),
61+
setMethod('PUT'),
62+
);
63+
64+
/**
65+
* Deletes a Stream.
66+
*
67+
* @param streamId { number } The ID of the Stream.
68+
*/
69+
export const deleteStream = (streamId: number) =>
70+
Request<{}>(
71+
setURL(`${BETA_API_ROOT}/monitor/streams/${encodeURIComponent(streamId)}`),
72+
setMethod('DELETE'),
73+
);

packages/api-v4/src/datastream/types.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,24 @@ interface CustomHeader {
103103

104104
export interface CreateStreamPayload {
105105
destinations: number[];
106-
details?: StreamDetails;
106+
details: StreamDetails;
107107
label: string;
108108
status?: StreamStatus;
109109
type: StreamType;
110110
}
111111

112+
export interface UpdateStreamPayload {
113+
destinations: number[];
114+
details: StreamDetails;
115+
label: string;
116+
status: StreamStatus;
117+
type: StreamType;
118+
}
119+
120+
export interface UpdateStreamPayloadWithId extends UpdateStreamPayload {
121+
id: number;
122+
}
123+
112124
export interface CreateDestinationPayload {
113125
details: CustomHTTPsDetails | LinodeObjectStorageDetails;
114126
label: string;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
DataStreams: add actions with handlers in Streams list, add Edit Stream component ([#12645](https://github.com/linode/manager/pull/12645))

packages/manager/src/features/DataStream/DataStreamLanding.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import * as React from 'react';
22

33
import { DocumentTitleSegment } from 'src/components/DocumentTitle';
4-
import { LandingHeader } from 'src/components/LandingHeader';
4+
import {
5+
LandingHeader,
6+
type LandingHeaderProps,
7+
} from 'src/components/LandingHeader';
58
import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner';
69
import { SuspenseLoader } from 'src/components/SuspenseLoader';
710
import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel';
@@ -23,7 +26,7 @@ const Streams = React.lazy(() =>
2326
);
2427

2528
export const DataStreamLanding = React.memo(() => {
26-
const landingHeaderProps = {
29+
const landingHeaderProps: LandingHeaderProps = {
2730
breadcrumbProps: {
2831
pathname: '/datastream',
2932
},

packages/manager/src/features/DataStream/Streams/StreamCreate/Delivery/DestinationDetail.tsx renamed to packages/manager/src/features/DataStream/Shared/LabelValue.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,37 @@ import { Box, Typography } from '@linode/ui';
22
import { styled, useTheme } from '@mui/material/styles';
33
import * as React from 'react';
44

5-
type DestinationDetailProps = {
5+
type LabelValueProps = {
6+
compact?: boolean;
7+
'data-testid'?: string;
68
label: string;
79
value: string;
810
};
911

10-
export const DestinationDetail = (props: DestinationDetailProps) => {
11-
const { label, value } = props;
12+
export const LabelValue = (props: LabelValueProps) => {
13+
const { compact = false, label, value, 'data-testid': dataTestId } = props;
1214
const theme = useTheme();
1315

1416
return (
15-
<Box display="flex" marginTop={theme.spacingFunction(16)}>
16-
<StyledLabel>{label}:</StyledLabel>
17-
<StyledValue>{value}</StyledValue>
17+
<Box
18+
alignItems="center"
19+
display="flex"
20+
marginTop={theme.spacingFunction(16)}
21+
>
22+
<Typography
23+
sx={{
24+
mr: 1,
25+
font: theme.font.bold,
26+
width: compact ? 'auto' : 160,
27+
}}
28+
>
29+
{label}:
30+
</Typography>
31+
<StyledValue data-testid={dataTestId}>{value}</StyledValue>
1832
</Box>
1933
);
2034
};
2135

22-
const StyledLabel = styled(Typography, {
23-
label: 'StyledLabel',
24-
})(({ theme }) => ({
25-
font: theme.font.bold,
26-
width: 160,
27-
}));
28-
2936
const StyledValue = styled(Box, {
3037
label: 'StyledValue',
3138
})(({ theme }) => ({

packages/manager/src/features/DataStream/Shared/types.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
import { destinationType, streamStatus } from '@linode/api-v4';
1+
import { destinationType, streamStatus, streamType } from '@linode/api-v4';
22

33
import type { CreateDestinationPayload } from '@linode/api-v4';
44

5-
export interface DestinationTypeOption {
6-
label: string;
7-
value: string;
8-
}
5+
export type FormMode = 'create' | 'edit';
96

107
export interface LabelValueOption {
118
label: string;
129
value: string;
1310
}
1411

15-
export const destinationTypeOptions: DestinationTypeOption[] = [
12+
export const destinationTypeOptions: LabelValueOption[] = [
1613
{
1714
value: destinationType.CustomHttps,
1815
label: 'Custom HTTPS',
@@ -23,7 +20,18 @@ export const destinationTypeOptions: DestinationTypeOption[] = [
2320
},
2421
];
2522

26-
export const streamStatusOptions = [
23+
export const streamTypeOptions: LabelValueOption[] = [
24+
{
25+
value: streamType.AuditLogs,
26+
label: 'Audit Logs',
27+
},
28+
{
29+
value: streamType.LKEAuditLogs,
30+
label: 'Kubernetes Audit Logs',
31+
},
32+
];
33+
34+
export const streamStatusOptions: LabelValueOption[] = [
2735
{
2836
value: streamStatus.Active,
2937
label: 'Enabled',
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { screen } from '@testing-library/react';
2+
import { userEvent } from '@testing-library/user-event';
3+
import * as React from 'react';
4+
5+
import { streamFactory } from 'src/factories/datastream';
6+
import { StreamActionMenu } from 'src/features/DataStream/Streams/StreamActionMenu';
7+
import { renderWithTheme } from 'src/utilities/testHelpers';
8+
9+
import type { StreamStatus } from '@linode/api-v4';
10+
11+
const fakeHandler = vi.fn();
12+
13+
describe('Stream action menu', () => {
14+
const renderComponent = (status: StreamStatus) => {
15+
renderWithTheme(
16+
<StreamActionMenu
17+
onDelete={fakeHandler}
18+
onDisableOrEnable={fakeHandler}
19+
onEdit={fakeHandler}
20+
stream={streamFactory.build({ status })}
21+
/>
22+
);
23+
};
24+
25+
describe('when stream is active', () => {
26+
it('should include proper Stream actions', async () => {
27+
renderComponent('active');
28+
29+
const actionMenuButton = screen.queryByLabelText(/^Action menu for/)!;
30+
31+
await userEvent.click(actionMenuButton);
32+
33+
for (const action of ['Edit', 'Disable', 'Delete']) {
34+
expect(screen.getByText(action)).toBeVisible();
35+
}
36+
});
37+
});
38+
39+
describe('when stream is inactive', () => {
40+
it('should include proper Stream actions', async () => {
41+
renderComponent('inactive');
42+
43+
const actionMenuButton = screen.queryByLabelText(/^Action menu for/)!;
44+
45+
await userEvent.click(actionMenuButton);
46+
47+
for (const action of ['Edit', 'Enable', 'Delete']) {
48+
expect(screen.getByText(action)).toBeVisible();
49+
}
50+
});
51+
});
52+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { type Stream, streamStatus } from '@linode/api-v4';
2+
import * as React from 'react';
3+
4+
import { ActionMenu } from 'src/components/ActionMenu/ActionMenu';
5+
6+
export interface Handlers {
7+
onDelete: (stream: Stream) => void;
8+
onDisableOrEnable: (stream: Stream) => void;
9+
onEdit: (stream: Stream) => void;
10+
}
11+
12+
interface StreamActionMenuProps extends Handlers {
13+
stream: Stream;
14+
}
15+
16+
export const StreamActionMenu = (props: StreamActionMenuProps) => {
17+
const { stream, onDelete, onDisableOrEnable, onEdit } = props;
18+
19+
const menuActions = [
20+
{
21+
onClick: () => {
22+
onEdit(stream);
23+
},
24+
title: 'Edit',
25+
},
26+
{
27+
onClick: () => {
28+
onDisableOrEnable(stream);
29+
},
30+
title: stream.status === streamStatus.Active ? 'Disable' : 'Enable',
31+
},
32+
{
33+
onClick: () => {
34+
onDelete(stream);
35+
},
36+
title: 'Delete',
37+
},
38+
];
39+
40+
return (
41+
<ActionMenu
42+
actionsList={menuActions}
43+
ariaLabel={`Action menu for Stream ${stream.label}`}
44+
/>
45+
);
46+
};

0 commit comments

Comments
 (0)