Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions redisinsight/ui/src/constants/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ enum ApiEndpoints {
STREAMS_ENTRIES_GET = 'streams/entries/get',
STREAMS_CONSUMER_GROUPS = 'streams/consumer-groups',
STREAMS_CONSUMER_GROUPS_GET = 'streams/consumer-groups/get',
STREAMS_CONSUMERS = 'streams/consumer-groups/consumers',
STREAMS_CONSUMERS_GET = 'streams/consumer-groups/consumers/get',
STREAMS_CONSUMERS_MESSAGES_GET = 'streams/consumer-groups/consumers/pending-messages/get',
STREAMS = 'streams',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const PopoverDelete = (props: Props) => {
data-testid={testid ? `${testid}-icon` : 'remove-icon'}
/>
)}
onClick={(e) => e.stopPropagation()}
>
<div className={styles.popover}>
<EuiText size="m">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import React from 'react'
import { instance, mock } from 'ts-mockito'
import { cloneDeep } from 'lodash'
import { cleanup, fireEvent, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
import { loadConsumerGroups, setSelectedConsumer } from 'uiSrc/slices/browser/stream'
import {
deleteConsumers,
loadConsumerGroups,
setSelectedConsumer
} from 'uiSrc/slices/browser/stream'
import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable'
import { ConsumerDto } from 'apiSrc/modules/browser/dto/stream.dto'
import ConsumersView, { Props as ConsumersViewProps } from './ConsumersView'
Expand Down Expand Up @@ -77,4 +81,15 @@ describe('ConsumersViewWrapper', () => {

expect(store.getActions()).toEqual([...afterRenderActions, setSelectedConsumer(), loadConsumerGroups(false)])
})

it('should delete Consumer', () => {
render(<ConsumersViewWrapper {...instance(mockedProps)} />)

const afterRenderActions = [...store.getActions()]

fireEvent.click(screen.getByTestId('remove-consumer-button-test-icon'))
fireEvent.click(screen.getByTestId('remove-consumer-button-test'))

expect(store.getActions()).toEqual([...afterRenderActions, deleteConsumers()])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ import React, { useCallback, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'

import {
deleteStreamEntry,
setStreamViewType,
selectedGroupSelector,
setSelectedConsumer,
fetchConsumerMessages
fetchConsumerMessages,
deleteConsumersAction
} from 'uiSrc/slices/browser/stream'
import { ITableColumn } from 'uiSrc/components/virtual-table/interfaces'
import PopoverDelete from 'uiSrc/pages/browser/components/popover-delete/PopoverDelete'
import { TableCellAlignment, TableCellTextAlignment } from 'uiSrc/constants'
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'
import { numberWithSpaces } from 'uiSrc/utils/numbers'
import { updateSelectedKeyRefreshTime } from 'uiSrc/slices/browser/keys'
import { selectedKeyDataSelector, updateSelectedKeyRefreshTime } from 'uiSrc/slices/browser/keys'

import { ConsumerDto } from 'apiSrc/modules/browser/dto/stream.dto'
import ConsumersView from './ConsumersView'
Expand All @@ -29,8 +28,9 @@ export interface Props {
}

const ConsumersViewWrapper = (props: Props) => {
const { name: key = '' } = useSelector(connectedInstanceSelector)
const { name: key = '' } = useSelector(selectedKeyDataSelector) ?? { name: '' }
const {
name: selectedGroupName = '',
lastRefreshTime,
data: loadedConsumers = [],
} = useSelector(selectedGroupSelector) ?? {}
Expand All @@ -52,7 +52,7 @@ const ConsumersViewWrapper = (props: Props) => {
}, [])

const handleDeleteConsumer = (consumerName = '') => {
dispatch(deleteStreamEntry(key, [consumerName]))
dispatch(deleteConsumersAction(key, selectedGroupName, [consumerName]))
closePopover()
}

Expand Down Expand Up @@ -120,11 +120,10 @@ const ConsumersViewWrapper = (props: Props) => {
return (
<div>
<PopoverDelete
header={name}
text={(
<>
Consumer will be removed from
<br />
{key}
will be removed from Consumer Group <b>{selectedGroupName}</b>
</>
)}
item={name}
Expand All @@ -134,7 +133,7 @@ const ConsumersViewWrapper = (props: Props) => {
updateLoading={false}
showPopover={showPopover}
testid={`remove-consumer-button-${name}`}
handleDeleteItem={handleDeleteConsumer}
handleDeleteItem={() => handleDeleteConsumer(name)}
handleButtonClick={handleRemoveIconClick}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import React from 'react'
import { instance, mock } from 'ts-mockito'
import { cloneDeep } from 'lodash'
import { cleanup, fireEvent, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
import { loadConsumerGroups, setSelectedGroup } from 'uiSrc/slices/browser/stream'
import {
deleteConsumerGroups,
loadConsumerGroups,
setSelectedGroup
} from 'uiSrc/slices/browser/stream'
import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable'
import { ConsumerGroupDto } from 'apiSrc/modules/browser/dto/stream.dto'
import GroupsView, { Props as GroupsViewProps } from './GroupsView'
Expand Down Expand Up @@ -83,4 +87,15 @@ describe('GroupsViewWrapper', () => {

expect(store.getActions()).toEqual([...afterRenderActions, setSelectedGroup(), loadConsumerGroups(false)])
})

it('should delete Group', () => {
render(<GroupsViewWrapper {...instance(mockedProps)} />)

const afterRenderActions = [...store.getActions()]

fireEvent.click(screen.getByTestId('remove-groups-button-test-icon'))
fireEvent.click(screen.getByTestId('remove-groups-button-test'))

expect(store.getActions()).toEqual([...afterRenderActions, deleteConsumerGroups()])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import {
fetchConsumers,
setStreamViewType,
modifyLastDeliveredIdAction,
deleteConsumerGroupsAction,
} from 'uiSrc/slices/browser/stream'
import { ITableColumn } from 'uiSrc/components/virtual-table/interfaces'
import PopoverDelete from 'uiSrc/pages/browser/components/popover-delete/PopoverDelete'
import { consumerGroupIdRegex, validateConsumerGroupId } from 'uiSrc/utils'
import { getFormatTime } from 'uiSrc/utils/streamUtils'
import { TableCellTextAlignment } from 'uiSrc/constants'
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'

import { ConsumerGroupDto, UpdateConsumerGroupDto } from 'apiSrc/modules/browser/dto/stream.dto'
Expand All @@ -43,8 +43,7 @@ const GroupsViewWrapper = (props: Props) => {
data: loadedGroups = [],
loading
} = useSelector(streamGroupsSelector)
const { name: key = '' } = useSelector(connectedInstanceSelector)
const { name: selectedKey } = useSelector(selectedKeyDataSelector) ?? {}
const { name: selectedKey } = useSelector(selectedKeyDataSelector) ?? { name: '' }

const dispatch = useDispatch()

Expand Down Expand Up @@ -83,8 +82,8 @@ const GroupsViewWrapper = (props: Props) => {
setDeleting(`${groupName + suffix}`)
}, [])

const handleDeleteGroup = () => {
// dispatch(deleteStreamEntry(key, [groupName]))
const handleDeleteGroup = (name: string) => {
dispatch(deleteConsumerGroupsAction(selectedKey, [name]))
closePopover()
}

Expand Down Expand Up @@ -248,11 +247,10 @@ const GroupsViewWrapper = (props: Props) => {
</>
</PopoverItemEditor>
<PopoverDelete
header={name}
text={(
<>
Group will be removed from
<br />
{key}
will be removed from <b>{selectedKey}</b>
</>
)}
item={name}
Expand All @@ -262,7 +260,7 @@ const GroupsViewWrapper = (props: Props) => {
updateLoading={false}
showPopover={showPopover}
testid={`remove-groups-button-${name}`}
handleDeleteItem={handleDeleteGroup}
handleDeleteItem={() => handleDeleteGroup(name)}
handleButtonClick={handleRemoveIconClick}
/>
</div>
Expand Down
120 changes: 119 additions & 1 deletion redisinsight/ui/src/slices/browser/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,21 @@ const streamSlice = createSlice({
state.groups.loading = false
state.groups.error = payload
},

deleteConsumerGroups: (state) => {
state.groups.loading = true
state.groups.error = ''
},

deleteConsumerGroupsSuccess: (state) => {
state.groups.loading = false
},

deleteConsumerGroupsFailure: (state, { payload }) => {
state.groups.loading = false
state.groups.error = payload
},

setSelectedGroup: (state, { payload }) => {
state.groups.selectedGroup = payload
},
Expand Down Expand Up @@ -208,6 +223,20 @@ const streamSlice = createSlice({
state.viewType = StreamViewType.Groups
},

deleteConsumers: (state) => {
state.groups.loading = true
state.groups.error = ''
},

deleteConsumersSuccess: (state) => {
state.groups.loading = false
},

deleteConsumersFailure: (state, { payload }) => {
state.groups.loading = false
state.groups.error = payload
},

loadConsumerMessagesSuccess: (state, { payload }: PayloadAction<PendingEntryDto[]>) => {
state.groups.loading = false

Expand Down Expand Up @@ -262,11 +291,17 @@ export const {
loadConsumerGroups,
loadConsumerGroupsSuccess,
loadConsumerGroupsFailure,
deleteConsumerGroups,
deleteConsumerGroupsSuccess,
deleteConsumerGroupsFailure,
modifyLastDeliveredId,
modifyLastDeliveredIdSuccess,
modifyLastDeliveredIdFailure,
loadConsumersSuccess,
loadConsumersFailure,
deleteConsumers,
deleteConsumersSuccess,
deleteConsumersFailure,
loadConsumerMessagesSuccess,
loadConsumerMessagesFailure,
setSelectedGroup,
Expand Down Expand Up @@ -606,6 +641,44 @@ export function fetchConsumerGroups(
}
}

export function deleteConsumerGroupsAction(keyName: string, consumerGroups: string[], onSuccessAction?: () => void) {
return async (dispatch: AppDispatch, stateInit: () => RootState) => {
dispatch(deleteConsumerGroups())
try {
const state = stateInit()
const { status } = await apiService.delete(
getUrl(
state.connections.instances.connectedInstance?.id,
ApiEndpoints.STREAMS_CONSUMER_GROUPS
),
{
data: {
keyName,
consumerGroups,
},
}
)
if (isStatusSuccessful(status)) {
onSuccessAction?.()
dispatch(deleteConsumerGroupsSuccess())
dispatch<any>(fetchConsumerGroups(false))
dispatch<any>(refreshKeyInfoAction(keyName))
dispatch(addMessageNotification(
successMessages.REMOVED_KEY_VALUE(
keyName,
consumerGroups.join(''),
'Group'
)
))
}
} catch (error) {
const errorMessage = getApiErrorMessage(error)
dispatch(addErrorNotification(error))
dispatch(deleteConsumerGroupsFailure(errorMessage))
}
}
}

// Asynchronous thunk action
export function fetchConsumers(
resetData?: boolean,
Expand All @@ -630,7 +703,7 @@ export function fetchConsumers(

if (isStatusSuccessful(status)) {
dispatch(loadConsumersSuccess(data))
onSuccess?.(data)
onSuccess?.()
}
} catch (_err) {
if (!axios.isCancel(_err)) {
Expand All @@ -644,6 +717,51 @@ export function fetchConsumers(
}
}

// Asynchronous thunk action
export function deleteConsumersAction(
keyName: string,
groupName: string,
consumerNames: string[],
onSuccessAction?: () => void
) {
return async (dispatch: AppDispatch, stateInit: () => RootState) => {
dispatch(deleteConsumers())
try {
const state = stateInit()
const { status } = await apiService.delete(
getUrl(
state.connections.instances.connectedInstance?.id,
ApiEndpoints.STREAMS_CONSUMERS
),
{
data: {
keyName,
groupName,
consumerNames,
},
}
)
if (isStatusSuccessful(status)) {
onSuccessAction?.()
dispatch(deleteConsumersSuccess())
dispatch<any>(fetchConsumers(false))
dispatch<any>(refreshKeyInfoAction(keyName))
dispatch(addMessageNotification(
successMessages.REMOVED_KEY_VALUE(
keyName,
consumerNames.join(''),
'Consumer'
)
))
}
} catch (error) {
const errorMessage = getApiErrorMessage(error)
dispatch(addErrorNotification(error))
dispatch(deleteConsumersFailure(errorMessage))
}
}
}

// Asynchronous thunk action
export function fetchConsumerMessages(
resetData?: boolean,
Expand Down
Loading