diff --git a/packages/api-v4/src/delivery/types.ts b/packages/api-v4/src/delivery/types.ts index 384cf4a7b26..97f2202f0b9 100644 --- a/packages/api-v4/src/delivery/types.ts +++ b/packages/api-v4/src/delivery/types.ts @@ -1,5 +1,7 @@ export const streamStatus = { Active: 'active', + Deactivating: 'deactivating', + Failed: 'failed', Inactive: 'inactive', Provisioning: 'provisioning', } as const; diff --git a/packages/manager/.changeset/pr-13551-added-1774974854479.md b/packages/manager/.changeset/pr-13551-added-1774974854479.md new file mode 100644 index 00000000000..882d080c970 --- /dev/null +++ b/packages/manager/.changeset/pr-13551-added-1774974854479.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +New stream statuses - deactivating, failed ([#13551](https://github.com/linode/manager/pull/13551)) diff --git a/packages/manager/src/features/Delivery/Shared/types.ts b/packages/manager/src/features/Delivery/Shared/types.ts index 9373632cf7c..bddacb55f08 100644 --- a/packages/manager/src/features/Delivery/Shared/types.ts +++ b/packages/manager/src/features/Delivery/Shared/types.ts @@ -49,6 +49,16 @@ export const streamStatusOptions: AutocompleteOption[] = [ label: 'Active', pendoId: 'Logs Delivery Streams-Status Active', }, + { + value: streamStatus.Deactivating, + label: 'Deactivating', + pendoId: 'Logs Delivery Streams-Status Deactivating', + }, + { + value: streamStatus.Failed, + label: 'Failed', + pendoId: 'Logs Delivery Streams-Status Failed', + }, { value: streamStatus.Inactive, label: 'Inactive', diff --git a/packages/manager/src/features/Delivery/Streams/StreamActionMenu.tsx b/packages/manager/src/features/Delivery/Streams/StreamActionMenu.tsx index 97078a82672..8b4c20396ff 100644 --- a/packages/manager/src/features/Delivery/Streams/StreamActionMenu.tsx +++ b/packages/manager/src/features/Delivery/Streams/StreamActionMenu.tsx @@ -17,6 +17,7 @@ interface StreamActionMenuProps extends StreamHandlers { export const StreamActionMenu = (props: StreamActionMenuProps) => { const { stream, onDelete, onDisableOrEnable, onEdit } = props; + const { status, label } = stream; const menuActions: Action[] = [ { @@ -30,9 +31,12 @@ export const StreamActionMenu = (props: StreamActionMenuProps) => { onClick: () => { onDisableOrEnable(stream); }, - title: stream.status === streamStatus.Active ? 'Deactivate' : 'Activate', - pendoId: `Logs Delivery Streams-${stream.status === streamStatus.Active ? 'Deactivate' : 'Activate'}`, - disabled: stream.status === streamStatus.Provisioning, + title: status === streamStatus.Active ? 'Deactivate' : 'Activate', + pendoId: `Logs Delivery Streams-${status === streamStatus.Active ? 'Deactivate' : 'Activate'}`, + disabled: + status === streamStatus.Deactivating || + status === streamStatus.Failed || + status === streamStatus.Provisioning, }, { onClick: () => { @@ -46,7 +50,7 @@ export const StreamActionMenu = (props: StreamActionMenuProps) => { return ( ); diff --git a/packages/manager/src/features/Delivery/Streams/StreamForm/StreamEdit.test.tsx b/packages/manager/src/features/Delivery/Streams/StreamForm/StreamEdit.test.tsx index 4f2850ff542..be8fb54644f 100644 --- a/packages/manager/src/features/Delivery/Streams/StreamForm/StreamEdit.test.tsx +++ b/packages/manager/src/features/Delivery/Streams/StreamForm/StreamEdit.test.tsx @@ -1,3 +1,4 @@ +import { streamStatus } from '@linode/api-v4'; import { screen, waitFor, @@ -241,47 +242,63 @@ describe('StreamEdit', () => { }); }); - describe('and stream has status: provisioning', () => { - it('should have disabled Edit Stream button and show info tooltip', async () => { - server.use( - http.get('*/monitor/streams/destinations', () => { - return HttpResponse.json(makeResourcePage(mockDestinations)); - }), - http.get(`*/monitor/streams/${streamId}`, () => { - return HttpResponse.json({ - ...mockStream, - status: 'provisioning', - }); - }) - ); - - renderWithThemeAndHookFormContext({ - component: , + const blockingStatuses = [ + streamStatus.Deactivating, + streamStatus.Failed, + streamStatus.Provisioning, + ]; + + describe.each(blockingStatuses)( + 'and stream has status: %status', + (status) => { + it('should have disabled Edit Stream button and show info tooltip', async () => { + server.use( + http.get('*/monitor/streams/destinations', () => { + return HttpResponse.json( + makeResourcePage(mockDestinations) + ); + }), + http.get(`*/monitor/streams/${streamId}`, () => { + return HttpResponse.json({ + ...mockStream, + status, + }); + }) + ); + + renderWithThemeAndHookFormContext({ + component: , + }); + const loadingElement = screen.queryByTestId(loadingTestId); + await waitForElementToBeRemoved(loadingElement); + + const editStreamButton = screen.getByRole('button', { + name: saveStreamButtonText, + }); + + // Edit stream button should be disabled + expect(editStreamButton).toBeDisabled(); + + // Edit stream + await userEvent.hover(editStreamButton); + await screen.findByRole('tooltip'); + + screen.getByText((content) => + content.includes( + `You cannot save changes while the stream status is ${status}` + ) + ); + + const disabledButtonTooltip = screen.getByText((content) => + content.includes( + `You cannot save changes while the stream status is ${status}` + ) + ); + + expect(disabledButtonTooltip).toBeInTheDocument(); }); - const loadingElement = screen.queryByTestId(loadingTestId); - await waitForElementToBeRemoved(loadingElement); - - const editStreamButton = screen.getByRole('button', { - name: saveStreamButtonText, - }); - - // Edit stream button should be disabled - expect(editStreamButton).toBeDisabled(); - - // Edit stream - await userEvent.hover(editStreamButton); - - await waitFor(() => { - expect(screen.getByRole('tooltip')).toBeInTheDocument(); - }); - - const disabledButtonTooltip = screen.getByText( - 'You cannot save changes while the stream is provisioning.' - ); - - expect(disabledButtonTooltip).toBeInTheDocument(); - }); - }); + } + ); }); }); diff --git a/packages/manager/src/features/Delivery/Streams/StreamForm/StreamForm.tsx b/packages/manager/src/features/Delivery/Streams/StreamForm/StreamForm.tsx index 8ede47204b6..c046a35c226 100644 --- a/packages/manager/src/features/Delivery/Streams/StreamForm/StreamForm.tsx +++ b/packages/manager/src/features/Delivery/Streams/StreamForm/StreamForm.tsx @@ -77,12 +77,23 @@ export const StreamForm = (props: StreamFormProps) => { control, name: 'stream.status', }); + + const isStreamStatusBlocking = + !!selectedStreamStatus && + ( + [ + streamStatus.Provisioning, + streamStatus.Deactivating, + streamStatus.Failed, + ] as StreamStatus[] + ).includes(selectedStreamStatus); + const submitButtonTooltip = useMemo( () => - selectedStreamStatus === streamStatus.Provisioning - ? 'You cannot save changes while the stream is provisioning.' + isStreamStatusBlocking + ? `You cannot save changes while the stream status is ${selectedStreamStatus}` : undefined, - [selectedStreamStatus] + [isStreamStatusBlocking, selectedStreamStatus] ); useEffect(() => { @@ -212,8 +223,7 @@ export const StreamForm = (props: StreamFormProps) => { { const { stream, onDelete, onDisableOrEnable, onEdit } = props; const { id, status } = stream; - const iconStatus = ( - ['active', 'error', 'inactive'].includes(status) ? status : 'other' - ) as Status; + const iconStatus = ((): Status => { + if (status === 'failed') return 'error'; + if (['active', 'error', 'inactive'].includes(status)) { + return status as Status; + } + return 'other'; + })(); return ( @@ -72,6 +76,10 @@ const humanizeStreamStatus = (status: StreamStatus) => { switch (status) { case 'active': return 'Active'; + case 'deactivating': + return 'Deactivating'; + case 'failed': + return 'Failed'; case 'inactive': return 'Inactive'; case 'provisioning': diff --git a/packages/validation/src/delivery.schema.ts b/packages/validation/src/delivery.schema.ts index 530cdfafa85..06b36f37420 100644 --- a/packages/validation/src/delivery.schema.ts +++ b/packages/validation/src/delivery.schema.ts @@ -351,11 +351,9 @@ const streamSchemaBase = object({ .min(3, 'Stream name must have at least 3 characters.') .max(maxLength, maxLengthMessage) .required('Stream name is required.'), - status: mixed<'active' | 'inactive' | 'provisioning'>().oneOf([ - 'active', - 'inactive', - 'provisioning', - ]), + status: mixed< + 'active' | 'deactivating' | 'failed' | 'inactive' | 'provisioning' + >().oneOf(['active', 'deactivating', 'failed', 'inactive', 'provisioning']), type: string() .oneOf(['audit_logs', 'lke_audit_logs']) .required('Stream type is required.'), @@ -372,8 +370,10 @@ export const createStreamSchema = streamSchemaBase; export const updateStreamSchema = streamSchemaBase .omit(['type']) .shape({ - status: mixed<'active' | 'inactive' | 'provisioning'>() - .oneOf(['active', 'inactive', 'provisioning']) + status: mixed< + 'active' | 'deactivating' | 'failed' | 'inactive' | 'provisioning' + >() + .oneOf(['active', 'deactivating', 'failed', 'inactive', 'provisioning']) .required(), details: lazy((value) => { if (