Skip to content

Commit f386b54

Browse files
authored
Merge pull request #720 from RedisInsight/feature/RI-2938_RI-2940
#RI-2938, #RI-2940
2 parents cb81eed + 1299bbe commit f386b54

File tree

8 files changed

+304
-34
lines changed

8 files changed

+304
-34
lines changed

redisinsight/ui/src/constants/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum ApiEndpoints {
3737
STREAMS_ENTRIES_GET = 'streams/entries/get',
3838
STREAMS_CONSUMER_GROUPS = 'streams/consumer-groups',
3939
STREAMS_CONSUMER_GROUPS_GET = 'streams/consumer-groups/get',
40+
STREAMS_CONSUMERS = 'streams/consumer-groups/consumers',
4041
STREAMS_CONSUMERS_GET = 'streams/consumer-groups/consumers/get',
4142
STREAMS_CONSUMERS_MESSAGES_GET = 'streams/consumer-groups/consumers/pending-messages/get',
4243
STREAMS = 'streams',

redisinsight/ui/src/pages/browser/components/popover-delete/PopoverDelete.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const PopoverDelete = (props: Props) => {
6363
data-testid={testid ? `${testid}-icon` : 'remove-icon'}
6464
/>
6565
)}
66+
onClick={(e) => e.stopPropagation()}
6667
>
6768
<div className={styles.popover}>
6869
<EuiText size="m">

redisinsight/ui/src/pages/browser/components/stream-details/consumers-view/ConsumersViewWrapper.spec.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33
import { cloneDeep } from 'lodash'
44
import { cleanup, fireEvent, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
5-
import { loadConsumerGroups, setSelectedConsumer } from 'uiSrc/slices/browser/stream'
5+
import {
6+
deleteConsumers,
7+
loadConsumerGroups,
8+
setSelectedConsumer
9+
} from 'uiSrc/slices/browser/stream'
610
import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable'
711
import { ConsumerDto } from 'apiSrc/modules/browser/dto/stream.dto'
812
import ConsumersView, { Props as ConsumersViewProps } from './ConsumersView'
@@ -77,4 +81,15 @@ describe('ConsumersViewWrapper', () => {
7781

7882
expect(store.getActions()).toEqual([...afterRenderActions, setSelectedConsumer(), loadConsumerGroups(false)])
7983
})
84+
85+
it('should delete Consumer', () => {
86+
render(<ConsumersViewWrapper {...instance(mockedProps)} />)
87+
88+
const afterRenderActions = [...store.getActions()]
89+
90+
fireEvent.click(screen.getByTestId('remove-consumer-button-test-icon'))
91+
fireEvent.click(screen.getByTestId('remove-consumer-button-test'))
92+
93+
expect(store.getActions()).toEqual([...afterRenderActions, deleteConsumers()])
94+
})
8095
})

redisinsight/ui/src/pages/browser/components/stream-details/consumers-view/ConsumersViewWrapper.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@ import React, { useCallback, useEffect, useState } from 'react'
22
import { useDispatch, useSelector } from 'react-redux'
33

44
import {
5-
deleteStreamEntry,
65
setStreamViewType,
76
selectedGroupSelector,
87
setSelectedConsumer,
9-
fetchConsumerMessages
8+
fetchConsumerMessages,
9+
deleteConsumersAction
1010
} from 'uiSrc/slices/browser/stream'
1111
import { ITableColumn } from 'uiSrc/components/virtual-table/interfaces'
1212
import PopoverDelete from 'uiSrc/pages/browser/components/popover-delete/PopoverDelete'
1313
import { TableCellAlignment, TableCellTextAlignment } from 'uiSrc/constants'
14-
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
1514
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'
1615
import { numberWithSpaces } from 'uiSrc/utils/numbers'
17-
import { updateSelectedKeyRefreshTime } from 'uiSrc/slices/browser/keys'
16+
import { selectedKeyDataSelector, updateSelectedKeyRefreshTime } from 'uiSrc/slices/browser/keys'
1817

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

3130
const ConsumersViewWrapper = (props: Props) => {
32-
const { name: key = '' } = useSelector(connectedInstanceSelector)
31+
const { name: key = '' } = useSelector(selectedKeyDataSelector) ?? { name: '' }
3332
const {
33+
name: selectedGroupName = '',
3434
lastRefreshTime,
3535
data: loadedConsumers = [],
3636
} = useSelector(selectedGroupSelector) ?? {}
@@ -52,7 +52,7 @@ const ConsumersViewWrapper = (props: Props) => {
5252
}, [])
5353

5454
const handleDeleteConsumer = (consumerName = '') => {
55-
dispatch(deleteStreamEntry(key, [consumerName]))
55+
dispatch(deleteConsumersAction(key, selectedGroupName, [consumerName]))
5656
closePopover()
5757
}
5858

@@ -120,11 +120,10 @@ const ConsumersViewWrapper = (props: Props) => {
120120
return (
121121
<div>
122122
<PopoverDelete
123+
header={name}
123124
text={(
124125
<>
125-
Consumer will be removed from
126-
<br />
127-
{key}
126+
will be removed from Consumer Group <b>{selectedGroupName}</b>
128127
</>
129128
)}
130129
item={name}
@@ -134,7 +133,7 @@ const ConsumersViewWrapper = (props: Props) => {
134133
updateLoading={false}
135134
showPopover={showPopover}
136135
testid={`remove-consumer-button-${name}`}
137-
handleDeleteItem={handleDeleteConsumer}
136+
handleDeleteItem={() => handleDeleteConsumer(name)}
138137
handleButtonClick={handleRemoveIconClick}
139138
/>
140139
</div>

redisinsight/ui/src/pages/browser/components/stream-details/groups-view/GroupsViewWrapper.spec.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33
import { cloneDeep } from 'lodash'
44
import { cleanup, fireEvent, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
5-
import { loadConsumerGroups, setSelectedGroup } from 'uiSrc/slices/browser/stream'
5+
import {
6+
deleteConsumerGroups,
7+
loadConsumerGroups,
8+
setSelectedGroup
9+
} from 'uiSrc/slices/browser/stream'
610
import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable'
711
import { ConsumerGroupDto } from 'apiSrc/modules/browser/dto/stream.dto'
812
import GroupsView, { Props as GroupsViewProps } from './GroupsView'
@@ -83,4 +87,15 @@ describe('GroupsViewWrapper', () => {
8387

8488
expect(store.getActions()).toEqual([...afterRenderActions, setSelectedGroup(), loadConsumerGroups(false)])
8589
})
90+
91+
it('should delete Group', () => {
92+
render(<GroupsViewWrapper {...instance(mockedProps)} />)
93+
94+
const afterRenderActions = [...store.getActions()]
95+
96+
fireEvent.click(screen.getByTestId('remove-groups-button-test-icon'))
97+
fireEvent.click(screen.getByTestId('remove-groups-button-test'))
98+
99+
expect(store.getActions()).toEqual([...afterRenderActions, deleteConsumerGroups()])
100+
})
86101
})

redisinsight/ui/src/pages/browser/components/stream-details/groups-view/GroupsViewWrapper.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import {
1111
fetchConsumers,
1212
setStreamViewType,
1313
modifyLastDeliveredIdAction,
14+
deleteConsumerGroupsAction,
1415
} from 'uiSrc/slices/browser/stream'
1516
import { ITableColumn } from 'uiSrc/components/virtual-table/interfaces'
1617
import PopoverDelete from 'uiSrc/pages/browser/components/popover-delete/PopoverDelete'
1718
import { consumerGroupIdRegex, validateConsumerGroupId } from 'uiSrc/utils'
1819
import { getFormatTime } from 'uiSrc/utils/streamUtils'
1920
import { TableCellTextAlignment } from 'uiSrc/constants'
20-
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
2121
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'
2222

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

4948
const dispatch = useDispatch()
5049

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

86-
const handleDeleteGroup = () => {
87-
// dispatch(deleteStreamEntry(key, [groupName]))
85+
const handleDeleteGroup = (name: string) => {
86+
dispatch(deleteConsumerGroupsAction(selectedKey, [name]))
8887
closePopover()
8988
}
9089

@@ -248,11 +247,10 @@ const GroupsViewWrapper = (props: Props) => {
248247
</>
249248
</PopoverItemEditor>
250249
<PopoverDelete
250+
header={name}
251251
text={(
252252
<>
253-
Group will be removed from
254-
<br />
255-
{key}
253+
will be removed from <b>{selectedKey}</b>
256254
</>
257255
)}
258256
item={name}
@@ -262,7 +260,7 @@ const GroupsViewWrapper = (props: Props) => {
262260
updateLoading={false}
263261
showPopover={showPopover}
264262
testid={`remove-groups-button-${name}`}
265-
handleDeleteItem={handleDeleteGroup}
263+
handleDeleteItem={() => handleDeleteGroup(name)}
266264
handleButtonClick={handleRemoveIconClick}
267265
/>
268266
</div>

redisinsight/ui/src/slices/browser/stream.ts

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,21 @@ const streamSlice = createSlice({
168168
state.groups.loading = false
169169
state.groups.error = payload
170170
},
171+
172+
deleteConsumerGroups: (state) => {
173+
state.groups.loading = true
174+
state.groups.error = ''
175+
},
176+
177+
deleteConsumerGroupsSuccess: (state) => {
178+
state.groups.loading = false
179+
},
180+
181+
deleteConsumerGroupsFailure: (state, { payload }) => {
182+
state.groups.loading = false
183+
state.groups.error = payload
184+
},
185+
171186
setSelectedGroup: (state, { payload }) => {
172187
state.groups.selectedGroup = payload
173188
},
@@ -208,6 +223,20 @@ const streamSlice = createSlice({
208223
state.viewType = StreamViewType.Groups
209224
},
210225

226+
deleteConsumers: (state) => {
227+
state.groups.loading = true
228+
state.groups.error = ''
229+
},
230+
231+
deleteConsumersSuccess: (state) => {
232+
state.groups.loading = false
233+
},
234+
235+
deleteConsumersFailure: (state, { payload }) => {
236+
state.groups.loading = false
237+
state.groups.error = payload
238+
},
239+
211240
loadConsumerMessagesSuccess: (state, { payload }: PayloadAction<PendingEntryDto[]>) => {
212241
state.groups.loading = false
213242

@@ -262,11 +291,17 @@ export const {
262291
loadConsumerGroups,
263292
loadConsumerGroupsSuccess,
264293
loadConsumerGroupsFailure,
294+
deleteConsumerGroups,
295+
deleteConsumerGroupsSuccess,
296+
deleteConsumerGroupsFailure,
265297
modifyLastDeliveredId,
266298
modifyLastDeliveredIdSuccess,
267299
modifyLastDeliveredIdFailure,
268300
loadConsumersSuccess,
269301
loadConsumersFailure,
302+
deleteConsumers,
303+
deleteConsumersSuccess,
304+
deleteConsumersFailure,
270305
loadConsumerMessagesSuccess,
271306
loadConsumerMessagesFailure,
272307
setSelectedGroup,
@@ -606,6 +641,44 @@ export function fetchConsumerGroups(
606641
}
607642
}
608643

644+
export function deleteConsumerGroupsAction(keyName: string, consumerGroups: string[], onSuccessAction?: () => void) {
645+
return async (dispatch: AppDispatch, stateInit: () => RootState) => {
646+
dispatch(deleteConsumerGroups())
647+
try {
648+
const state = stateInit()
649+
const { status } = await apiService.delete(
650+
getUrl(
651+
state.connections.instances.connectedInstance?.id,
652+
ApiEndpoints.STREAMS_CONSUMER_GROUPS
653+
),
654+
{
655+
data: {
656+
keyName,
657+
consumerGroups,
658+
},
659+
}
660+
)
661+
if (isStatusSuccessful(status)) {
662+
onSuccessAction?.()
663+
dispatch(deleteConsumerGroupsSuccess())
664+
dispatch<any>(fetchConsumerGroups(false))
665+
dispatch<any>(refreshKeyInfoAction(keyName))
666+
dispatch(addMessageNotification(
667+
successMessages.REMOVED_KEY_VALUE(
668+
keyName,
669+
consumerGroups.join(''),
670+
'Group'
671+
)
672+
))
673+
}
674+
} catch (error) {
675+
const errorMessage = getApiErrorMessage(error)
676+
dispatch(addErrorNotification(error))
677+
dispatch(deleteConsumerGroupsFailure(errorMessage))
678+
}
679+
}
680+
}
681+
609682
// Asynchronous thunk action
610683
export function fetchConsumers(
611684
resetData?: boolean,
@@ -630,7 +703,7 @@ export function fetchConsumers(
630703

631704
if (isStatusSuccessful(status)) {
632705
dispatch(loadConsumersSuccess(data))
633-
onSuccess?.(data)
706+
onSuccess?.()
634707
}
635708
} catch (_err) {
636709
if (!axios.isCancel(_err)) {
@@ -644,6 +717,51 @@ export function fetchConsumers(
644717
}
645718
}
646719

720+
// Asynchronous thunk action
721+
export function deleteConsumersAction(
722+
keyName: string,
723+
groupName: string,
724+
consumerNames: string[],
725+
onSuccessAction?: () => void
726+
) {
727+
return async (dispatch: AppDispatch, stateInit: () => RootState) => {
728+
dispatch(deleteConsumers())
729+
try {
730+
const state = stateInit()
731+
const { status } = await apiService.delete(
732+
getUrl(
733+
state.connections.instances.connectedInstance?.id,
734+
ApiEndpoints.STREAMS_CONSUMERS
735+
),
736+
{
737+
data: {
738+
keyName,
739+
groupName,
740+
consumerNames,
741+
},
742+
}
743+
)
744+
if (isStatusSuccessful(status)) {
745+
onSuccessAction?.()
746+
dispatch(deleteConsumersSuccess())
747+
dispatch<any>(fetchConsumers(false))
748+
dispatch<any>(refreshKeyInfoAction(keyName))
749+
dispatch(addMessageNotification(
750+
successMessages.REMOVED_KEY_VALUE(
751+
keyName,
752+
consumerNames.join(''),
753+
'Consumer'
754+
)
755+
))
756+
}
757+
} catch (error) {
758+
const errorMessage = getApiErrorMessage(error)
759+
dispatch(addErrorNotification(error))
760+
dispatch(deleteConsumersFailure(errorMessage))
761+
}
762+
}
763+
}
764+
647765
// Asynchronous thunk action
648766
export function fetchConsumerMessages(
649767
resetData?: boolean,

0 commit comments

Comments
 (0)