Skip to content

Commit 6df7166

Browse files
committed
Merge branch 'feature/RI-2932_Consumer_Groups' into feature/RI-810_create_consumer_groups
# Conflicts: # redisinsight/ui/src/constants/api.ts # redisinsight/ui/src/slices/browser/stream.ts # redisinsight/ui/src/slices/interfaces/stream.ts
2 parents 9809b19 + 3ed7c48 commit 6df7166

33 files changed

+1446
-252
lines changed

redisinsight/ui/src/components/page-placeholder/PagePlaceholder.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ import { ReactComponent as LogoIcon } from 'uiSrc/assets/img/logo.svg'
33
import { EuiLoadingLogo, EuiEmptyPrompt } from '@elastic/eui'
44

55
const PagePlaceholder = () => (
6-
<EuiEmptyPrompt
7-
icon={<EuiLoadingLogo logo={LogoIcon} size="xl" style={{ fontSize: '40px' }} />}
8-
titleSize="s"
9-
/>
6+
<>
7+
{ process.env.NODE_ENV !== 'development' && (
8+
<EuiEmptyPrompt
9+
icon={<EuiLoadingLogo logo={LogoIcon} size="xl" style={{ fontSize: '40px' }} />}
10+
titleSize="s"
11+
/>
12+
)}
13+
</>
14+
1015
)
1116

1217
export default PagePlaceholder

redisinsight/ui/src/pages/browser/components/hash-details/HashDetails.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ const HashDetails = (props: Props) => {
159159
})
160160
}
161161
setMatch(match)
162-
dispatch(fetchHashFields(key, 0, SCAN_COUNT_DEFAULT, match || matchAllValue, onSuccess))
162+
dispatch(fetchHashFields(key, 0, SCAN_COUNT_DEFAULT, match || matchAllValue, true, onSuccess))
163163
}
164164
}
165165

redisinsight/ui/src/pages/browser/components/stream-details/StreamDetails/index.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33
import { render } from 'uiSrc/utils/test-utils'
4-
import StreamDetailsWrapper, { Props } from './StreamDetailsWrapper'
4+
import StreamDataViewWrapper, { Props } from './StreamDetailsWrapper'
55

66
const mockedProps = mock<Props>()
77

88
describe('StreamDetailsWrapper', () => {
99
it('should render', () => {
10-
expect(render(<StreamDetailsWrapper {...instance(mockedProps)} />)).toBeTruthy()
10+
expect(render(<StreamDataViewWrapper {...instance(mockedProps)} />)).toBeTruthy()
1111
})
1212
})

redisinsight/ui/src/pages/browser/components/stream-details/StreamDetailsWrapper.tsx

Lines changed: 30 additions & 209 deletions
Original file line numberDiff line numberDiff line change
@@ -1,227 +1,48 @@
1-
import { EuiText, EuiToolTip } from '@elastic/eui'
2-
import React, { useCallback, useEffect, useState } from 'react'
1+
import { EuiProgress, EuiTab, EuiTabs } from '@elastic/eui'
2+
import React from 'react'
33
import { useDispatch, useSelector } from 'react-redux'
4-
import { keyBy } from 'lodash'
54

6-
import { formatLongName } from 'uiSrc/utils'
7-
import { streamDataSelector, deleteStreamEntry } from 'uiSrc/slices/browser/stream'
8-
import { ITableColumn } from 'uiSrc/components/virtual-table/interfaces'
9-
import PopoverDelete from 'uiSrc/pages/browser/components/popover-delete/PopoverDelete'
10-
import { getFormatTime } from 'uiSrc/utils/streamUtils'
11-
import { KeyTypes, TableCellTextAlignment } from 'uiSrc/constants'
12-
import { getBasedOnViewTypeEvent, sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
5+
import { streamSelector, setStreamViewType, streamGroupsSelector } from 'uiSrc/slices/browser/stream'
136
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
14-
import { keysSelector } from 'uiSrc/slices/browser/keys'
15-
import { StreamEntryDto } from 'apiSrc/modules/browser/dto/stream.dto'
7+
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'
168

17-
import StreamDetails from './StreamDetails'
9+
import { streamViewTypeTabs } from './constants'
10+
import ConsumersViewWrapper from './consumers-view'
11+
import GroupsViewWrapper from './groups-view'
12+
import StreamDataViewWrapper from './stream-data-view'
13+
import StreamTabs from './stream-tabs'
1814

19-
import styles from './StreamDetails/styles.module.scss'
20-
21-
export interface IStreamEntry extends StreamEntryDto {
22-
editing: boolean
23-
}
24-
25-
const suffix = '_stream'
26-
const actionsWidth = 50
27-
const minColumnWidth = 190
28-
29-
interface Props {
15+
export interface Props {
3016
isFooterOpen: boolean
3117
}
3218

3319
const StreamDetailsWrapper = (props: Props) => {
34-
const {
35-
entries: loadedEntries = [],
36-
keyName: key
37-
} = useSelector(streamDataSelector)
3820
const { id: instanceId } = useSelector(connectedInstanceSelector)
39-
const { viewType } = useSelector(keysSelector)
21+
const { viewType, loading } = useSelector(streamSelector)
22+
const { loading: loadingGroups } = useSelector(streamGroupsSelector)
4023

4124
const dispatch = useDispatch()
4225

43-
const [uniqFields, setUniqFields] = useState({})
44-
const [entries, setEntries] = useState<IStreamEntry[]>([])
45-
const [columns, setColumns] = useState<ITableColumn[]>([])
46-
const [deleting, setDeleting] = useState<string>('')
47-
48-
useEffect(() => {
49-
let fields = {}
50-
const streamEntries: IStreamEntry[] = loadedEntries?.map((item) => {
51-
fields = {
52-
...fields,
53-
...keyBy(Object.keys(item.fields))
54-
}
55-
56-
return {
57-
...item,
58-
editing: false,
59-
}
60-
})
61-
62-
setUniqFields(fields)
63-
setEntries(streamEntries)
64-
setColumns([idColumn, ...Object.keys(fields).map((field) => getTemplateColumn(field)), actionsColumn])
65-
}, [loadedEntries, deleting])
66-
67-
const closePopover = useCallback(() => {
68-
setDeleting('')
69-
}, [])
70-
71-
const showPopover = useCallback((entry = '') => {
72-
setDeleting(`${entry + suffix}`)
73-
}, [])
74-
75-
const onSuccessRemoved = () => {
76-
sendEventTelemetry({
77-
event: getBasedOnViewTypeEvent(
78-
viewType,
79-
TelemetryEvent.BROWSER_KEY_VALUE_REMOVED,
80-
TelemetryEvent.TREE_VIEW_KEY_VALUE_REMOVED
81-
),
82-
eventData: {
83-
databaseId: instanceId,
84-
keyType: KeyTypes.Stream,
85-
numberOfRemoved: 1,
86-
}
87-
})
88-
}
89-
90-
const handleDeleteEntry = (entryId = '') => {
91-
dispatch(deleteStreamEntry(key, [entryId], onSuccessRemoved))
92-
closePopover()
93-
}
94-
95-
const handleRemoveIconClick = () => {
96-
sendEventTelemetry({
97-
event: getBasedOnViewTypeEvent(
98-
viewType,
99-
TelemetryEvent.BROWSER_KEY_VALUE_REMOVE_CLICKED,
100-
TelemetryEvent.TREE_VIEW_KEY_VALUE_REMOVE_CLICKED
101-
),
102-
eventData: {
103-
databaseId: instanceId,
104-
keyType: KeyTypes.Stream
105-
}
106-
})
107-
}
108-
109-
const handleEditEntry = (entryId = '', editing: boolean) => {
110-
const newFieldsState = entries.map((item) => {
111-
if (item.id === entryId) {
112-
return { ...item, editing }
113-
}
114-
return item
115-
})
116-
setEntries(newFieldsState)
117-
}
118-
119-
const getTemplateColumn = (label: string) : ITableColumn => ({
120-
id: label,
121-
label,
122-
minWidth: minColumnWidth,
123-
isSortable: false,
124-
className: styles.cell,
125-
headerClassName: styles.cellHeader,
126-
headerCellClassName: 'truncateText',
127-
render: function Id(_name: string, { id, fields }: StreamEntryDto) {
128-
const value = fields[label] ?? ''
129-
const cellContent = value.substring(0, 200)
130-
const tooltipContent = formatLongName(value)
131-
132-
return (
133-
<EuiText size="s" style={{ maxWidth: '100%', minHeight: '36px' }}>
134-
<div
135-
style={{ display: 'flex' }}
136-
className="streamEntry"
137-
data-testid={`stream-entry-field-${id}`}
138-
>
139-
<EuiToolTip
140-
title="Value"
141-
className={styles.tooltip}
142-
anchorClassName="streamEntry line-clamp-2"
143-
position="bottom"
144-
content={tooltipContent}
145-
>
146-
<>{cellContent}</>
147-
</EuiToolTip>
148-
</div>
149-
</EuiText>
150-
)
151-
}
152-
})
153-
154-
const [idColumn, actionsColumn]: ITableColumn[] = [
155-
{
156-
id: 'id',
157-
label: 'Entry ID',
158-
absoluteWidth: minColumnWidth,
159-
minWidth: minColumnWidth,
160-
isSortable: true,
161-
className: styles.cell,
162-
headerClassName: styles.cellHeader,
163-
render: function Id(_name: string, { id }: StreamEntryDto) {
164-
const timestamp = id.split('-')?.[0]
165-
return (
166-
<div>
167-
<EuiText color="subdued" size="s" style={{ maxWidth: '100%' }}>
168-
<div className="truncateText streamEntry" style={{ display: 'flex' }} data-testid={`stream-entry-${id}-date`}>
169-
{getFormatTime(timestamp)}
170-
</div>
171-
</EuiText>
172-
<EuiText size="s" style={{ maxWidth: '100%' }}>
173-
<div className="streamEntryId" data-testid={`stream-entry-${id}`}>
174-
{id}
175-
</div>
176-
</EuiText>
177-
</div>
178-
)
179-
},
180-
},
181-
{
182-
id: 'actions',
183-
label: '',
184-
headerClassName: styles.actionsHeader,
185-
textAlignment: TableCellTextAlignment.Left,
186-
absoluteWidth: actionsWidth,
187-
maxWidth: actionsWidth,
188-
minWidth: actionsWidth,
189-
render: function Actions(_act: any, { id }: StreamEntryDto) {
190-
return (
191-
<div>
192-
<PopoverDelete
193-
text={(
194-
<>
195-
Entry {id} will be removed from
196-
<br />
197-
{key}
198-
</>
199-
)}
200-
item={id}
201-
suffix={suffix}
202-
deleting={deleting}
203-
closePopover={closePopover}
204-
updateLoading={false}
205-
showPopover={showPopover}
206-
testid={`remove-entry-button-${id}`}
207-
handleDeleteItem={handleDeleteEntry}
208-
handleButtonClick={handleRemoveIconClick}
209-
/>
210-
</div>
211-
)
212-
},
213-
},
214-
]
215-
21626
return (
21727
<>
218-
<StreamDetails
219-
data={entries}
220-
columns={columns}
221-
onEditEntry={handleEditEntry}
222-
onClosePopover={closePopover}
223-
{...props}
224-
/>
28+
{(loading || loadingGroups) && (
29+
<EuiProgress
30+
color="primary"
31+
size="xs"
32+
position="absolute"
33+
data-testid="progress-key-stream"
34+
/>
35+
)}
36+
<StreamTabs />
37+
{viewType === StreamViewType.Data && (
38+
<StreamDataViewWrapper {...props} />
39+
)}
40+
{viewType === StreamViewType.Groups && (
41+
<GroupsViewWrapper {...props} />
42+
)}
43+
{viewType === StreamViewType.Consumers && (
44+
<ConsumersViewWrapper {...props} />
45+
)}
22546
</>
22647
)
22748
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'
2+
3+
export const streamViewTypeTabs = [
4+
{
5+
id: StreamViewType.Data,
6+
label: 'Stream data',
7+
},
8+
{
9+
id: StreamViewType.Groups,
10+
label: 'Consumer Groups',
11+
},
12+
]
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33
import { render } from 'uiSrc/utils/test-utils'
4-
import StreamDetails, { Props } from './StreamDetails'
4+
import GroupsView, { Props } from './ConsumersView'
55

66
const mockedProps = mock<Props>()
77

8-
describe('StreamDetails', () => {
8+
describe('GroupsView', () => {
99
it('should render', () => {
10-
expect(render(<StreamDetails {...instance(mockedProps)} />)).toBeTruthy()
10+
expect(render(<GroupsView {...instance(mockedProps)} />)).toBeTruthy()
1111
})
1212
})

0 commit comments

Comments
 (0)