Skip to content

Commit 86f67ca

Browse files
committed
feat: quick record navigation
1 parent e65c4ba commit 86f67ca

File tree

6 files changed

+389
-50
lines changed

6 files changed

+389
-50
lines changed

ui/pages/consume_page/consume_page.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,13 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd {
9797
selectedRow := m.rows[m.table.Cursor()]
9898
selectedRecord := m.recordForRow(selectedRow)
9999
m.consuming = false
100+
recordIndex := m.recordIndexForRow(selectedRow)
100101
return m.navigator.ToRecordDetailsPage(
101102
tabs.LoadRecordDetailPageMsg{
102103
Record: &selectedRecord,
103104
TopicName: m.readDetails.TopicName,
105+
Records: m.records,
106+
Index: recordIndex,
104107
})
105108
}
106109
}
@@ -148,6 +151,18 @@ func (m *Model) recordForRow(row table.Row) kadmin.ConsumerRecord {
148151
panic(fmt.Sprintf("Record not found for row: %v", row))
149152
}
150153

154+
func (m *Model) recordIndexForRow(row table.Row) int {
155+
offset, _ := strconv.ParseInt(row[3], 10, 64)
156+
partition, _ := strconv.ParseInt(row[2], 10, 32)
157+
for i, rec := range m.records {
158+
if rec.Partition == partition &&
159+
rec.Offset == offset {
160+
return i
161+
}
162+
}
163+
panic(fmt.Sprintf("Record not found for row: %v", row))
164+
}
165+
151166
func (m *Model) createRows() []table.Row {
152167
var rows []table.Row
153168
for _, rec := range m.records {

ui/pages/consume_page/consume_page_test.go

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -653,18 +653,11 @@ func TestConsumptionPage(t *testing.T) {
653653
msgs := tests.ExecuteBatchCmd(cmd)
654654
assert.Len(t, msgs, 1)
655655
assert.IsType(t, tabs.ToRecordDetailsPageCalledMsg{}, msgs[0])
656-
assert.Equal(t, tabs.LoadRecordDetailPageMsg{
657-
Record: &kadmin.ConsumerRecord{
658-
Key: "key-9",
659-
Payload: serdes.DesData{},
660-
Err: nil,
661-
Partition: int64(9),
662-
Offset: int64(9),
663-
Headers: nil,
664-
Timestamp: now.Add(time.Duration(9) * time.Second),
665-
},
666-
TopicName: "topic1",
667-
}, msgs[0].(tabs.ToRecordDetailsPageCalledMsg).Msg)
656+
msg := msgs[0].(tabs.ToRecordDetailsPageCalledMsg).Msg
657+
assert.Equal(t, "key-9", msg.Record.Key)
658+
assert.Equal(t, "topic1", msg.TopicName)
659+
assert.Len(t, msg.Records, 10)
660+
assert.Equal(t, 9, msg.Index)
668661

669662
m.Update(tests.Key(tea.KeyF3))
670663
m.Update(tests.Key(tea.KeyLeft))
@@ -677,18 +670,11 @@ func TestConsumptionPage(t *testing.T) {
677670
msgs = tests.ExecuteBatchCmd(cmd)
678671
assert.Len(t, msgs, 1)
679672
assert.IsType(t, tabs.ToRecordDetailsPageCalledMsg{}, msgs[0])
680-
assert.Equal(t, tabs.LoadRecordDetailPageMsg{
681-
Record: &kadmin.ConsumerRecord{
682-
Key: "key-9",
683-
Payload: serdes.DesData{},
684-
Err: nil,
685-
Partition: int64(9),
686-
Offset: int64(9),
687-
Headers: nil,
688-
Timestamp: now.Add(time.Duration(9) * time.Second),
689-
},
690-
TopicName: "topic1",
691-
}, msgs[0].(tabs.ToRecordDetailsPageCalledMsg).Msg)
673+
msg = msgs[0].(tabs.ToRecordDetailsPageCalledMsg).Msg
674+
assert.Equal(t, "key-9", msg.Record.Key)
675+
assert.Equal(t, "topic1", msg.TopicName)
676+
assert.Len(t, msg.Records, 10)
677+
assert.Equal(t, 9, msg.Index)
692678
})
693679

694680
t.Run("Search by key", func(t *testing.T) {
@@ -761,7 +747,7 @@ func TestConsumptionPage(t *testing.T) {
761747
m.Update(kadmin.ConsumerRecordReceived{
762748
Records: records,
763749
})
764-
750+
765751
m.View(tests.NewKontext(), tests.Renderer)
766752

767753
kb := tests.NewKeyboard(m)

ui/pages/record_details_page/record_details_page.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const (
3939
type Model struct {
4040
notifierCmdbar *cmdbar.NotifierCmdBar
4141
record *kadmin.ConsumerRecord
42+
records []kadmin.ConsumerRecord
43+
recordIndex int
4244
recordVp *viewport.Model
4345
headerValueVp *viewport.Model
4446
topicName string
@@ -68,6 +70,12 @@ type CopyErrorMsg struct {
6870
Err error
6971
}
7072

73+
type NavigateToNextRecordMsg struct {
74+
}
75+
76+
type NavigateToPrevRecordMsg struct {
77+
}
78+
7179
func (m *Model) View(ktx *kontext.ProgramKtx, renderer *ui.Renderer) string {
7280

7381
notifierCmdbarView := m.notifierCmdbar.View(ktx, renderer)
@@ -102,6 +110,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd {
102110
switch msg.String() {
103111
case "esc":
104112
return ui.PublishMsg(nav.LoadCachedConsumptionPageMsg{})
113+
case "ctrl+n":
114+
cmds = m.handleNavigateToNext(cmds)
115+
case "ctrl+p":
116+
cmds = m.handleNavigateToPrev(cmds)
105117
case "h", "left", "right":
106118
if len(m.record.Headers) >= 1 {
107119
m.focus = !m.focus
@@ -117,6 +129,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd {
117129
default:
118130
cmds = m.updatedFocussedArea(msg, cmds)
119131
}
132+
case NavigateToNextRecordMsg:
133+
cmds = m.loadRecordAtIndex(m.recordIndex+1, cmds)
134+
case NavigateToPrevRecordMsg:
135+
cmds = m.loadRecordAtIndex(m.recordIndex-1, cmds)
120136
}
121137

122138
return tea.Batch(cmds...)
@@ -274,6 +290,66 @@ func (m *Model) handleCopy(cmds []tea.Cmd) []tea.Cmd {
274290
return cmds
275291
}
276292

293+
func (m *Model) handleNavigateToNext(cmds []tea.Cmd) []tea.Cmd {
294+
if m.recordIndex >= len(m.records)-1 {
295+
m.notifierCmdbar.Notifier.ShowError(fmt.Errorf("no more records"))
296+
return cmds
297+
}
298+
cmds = append(cmds, ui.PublishMsg(NavigateToNextRecordMsg{}))
299+
return cmds
300+
}
301+
302+
func (m *Model) handleNavigateToPrev(cmds []tea.Cmd) []tea.Cmd {
303+
if m.recordIndex <= 0 {
304+
m.notifierCmdbar.Notifier.ShowError(fmt.Errorf("no previous records"))
305+
return cmds
306+
}
307+
cmds = append(cmds, ui.PublishMsg(NavigateToPrevRecordMsg{}))
308+
return cmds
309+
}
310+
311+
func (m *Model) loadRecordAtIndex(index int, cmds []tea.Cmd) []tea.Cmd {
312+
if index < 0 || index >= len(m.records) {
313+
return cmds
314+
}
315+
m.record = &m.records[index]
316+
m.recordIndex = index
317+
m.resetViews()
318+
m.rebuildHeaderRows()
319+
m.updateMetaInfo()
320+
return cmds
321+
}
322+
323+
func (m *Model) rebuildHeaderRows() {
324+
sort.SliceStable(m.record.Headers, func(i, j int) bool {
325+
return m.record.Headers[i].Key < m.record.Headers[j].Key
326+
})
327+
m.headerRows = nil
328+
for _, header := range m.record.Headers {
329+
m.headerRows = append(m.headerRows, table.Row{header.Key})
330+
}
331+
}
332+
333+
func (m *Model) resetViews() {
334+
m.recordVp = nil
335+
m.schemaVp = nil
336+
}
337+
338+
func (m *Model) updateMetaInfo() {
339+
key := m.record.Key
340+
if key == "" {
341+
key = "<null>"
342+
}
343+
m.metaInfo = fmt.Sprintf("key: %s\ntimestamp: %s", key, m.record.Timestamp.Format(time.UnixDate))
344+
if m.record.Err != nil {
345+
m.err = m.record.Err
346+
m.notifierCmdbar.Notifier.ShowError(m.record.Err)
347+
} else {
348+
m.err = nil
349+
m.payload = ui.PrettyPrintJson(m.record.Payload.Value)
350+
}
351+
}
352+
277353
func (m *Model) updatedFocussedArea(msg tea.Msg, cmds []tea.Cmd) []tea.Cmd {
278354
// only update the component if no error is present
279355
if m.err != nil {
@@ -314,6 +390,13 @@ func (m *Model) Shortcuts() []statusbar.Shortcut {
314390
{"Copy " + whatToCopy, "c"},
315391
}
316392

393+
if len(m.records) > 1 {
394+
shortcuts = append(shortcuts, []statusbar.Shortcut{
395+
{"Next Record", "ctrl+n"},
396+
{"Prev Record", "ctrl+p"},
397+
}...)
398+
}
399+
317400
if m.config.ActiveCluster().HasSchemaRegistry() && m.focus == mainViewFocus {
318401
shortcuts = append(shortcuts, statusbar.Shortcut{
319402
Name: "Toggle Record/Schema",
@@ -337,6 +420,8 @@ func (m *Model) Title() string {
337420
func New(
338421
record *kadmin.ConsumerRecord,
339422
topicName string,
423+
records []kadmin.ConsumerRecord,
424+
recordIndex int,
340425
clipWriter clipper.Writer,
341426
ktx *kontext.ProgramKtx,
342427
) *Model {
@@ -402,6 +487,8 @@ func New(
402487

403488
return &Model{
404489
record: record,
490+
records: records,
491+
recordIndex: recordIndex,
405492
topicName: topicName,
406493
headerKeyTable: &headersTable,
407494
focus: mainViewFocus,

0 commit comments

Comments
 (0)