Skip to content

Commit 540f43f

Browse files
committed
feat: Add ConsumerInfoPage with JetStream consumer info display
1 parent c0e2449 commit 540f43f

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

consumer_info_page.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"time"
6+
7+
"github.com/gdamore/tcell/v2"
8+
"github.com/rivo/tview"
9+
"github.com/solidpulse/natsdash/ds"
10+
"github.com/solidpulse/natsdash/logger"
11+
"gopkg.in/yaml.v2"
12+
)
13+
14+
type ConsumerInfoPage struct {
15+
*tview.Flex
16+
Data *ds.Data
17+
app *tview.Application
18+
txtArea *tview.TextArea
19+
footerTxt *tview.TextView
20+
streamName string
21+
consumerName string
22+
}
23+
24+
func NewConsumerInfoPage(app *tview.Application, data *ds.Data) *ConsumerInfoPage {
25+
cip := &ConsumerInfoPage{
26+
Flex: tview.NewFlex().SetDirection(tview.FlexRow),
27+
app: app,
28+
Data: data,
29+
}
30+
31+
// Create header
32+
headerRow := tview.NewFlex().SetDirection(tview.FlexColumn)
33+
headerRow.AddItem(createTextView("[ESC] Back", tcell.ColorWhite), 0, 1, false)
34+
35+
// Create text area
36+
cip.txtArea = tview.NewTextArea()
37+
cip.txtArea.SetBorder(true)
38+
39+
// Create footer
40+
cip.footerTxt = createTextView("", tcell.ColorWhite)
41+
42+
// Add all components
43+
cip.AddItem(headerRow, 1, 0, false).
44+
AddItem(cip.txtArea, 0, 1, true).
45+
AddItem(cip.footerTxt, 1, 0, false)
46+
47+
cip.setupInputCapture()
48+
49+
return cip
50+
}
51+
52+
func (cip *ConsumerInfoPage) redraw(ctx *ds.Context) {
53+
cip.txtArea.SetTitle("Consumer Info: " + cip.consumerName)
54+
55+
// Get JetStream context
56+
js, err := ctx.Conn.JetStream()
57+
if err != nil {
58+
cip.notify("Failed to get JetStream context: "+err.Error(), 3*time.Second, "error")
59+
return
60+
}
61+
62+
// Get consumer info
63+
consumer, err := js.ConsumerInfo(cip.streamName, cip.consumerName)
64+
if err != nil {
65+
cip.notify("Failed to get consumer info: "+err.Error(), 3*time.Second, "error")
66+
return
67+
}
68+
69+
// Convert to map first
70+
var infoMap map[string]interface{}
71+
jsonBytes, err := json.Marshal(consumer)
72+
if err != nil {
73+
cip.notify("Failed to convert info: "+err.Error(), 3*time.Second, "error")
74+
return
75+
}
76+
if err := json.Unmarshal(jsonBytes, &infoMap); err != nil {
77+
cip.notify("Failed to process info: "+err.Error(), 3*time.Second, "error")
78+
return
79+
}
80+
81+
// Convert duration fields to strings
82+
if config, ok := infoMap["config"].(map[string]interface{}); ok {
83+
if ackWait, ok := config["ack_wait"].(float64); ok {
84+
config["ack_wait"] = time.Duration(ackWait).String()
85+
}
86+
if idleHeartbeat, ok := config["idle_heartbeat"].(float64); ok {
87+
config["idle_heartbeat"] = time.Duration(idleHeartbeat).String()
88+
}
89+
}
90+
91+
// Convert to YAML
92+
yamlBytes, err := yaml.Marshal(infoMap)
93+
if err != nil {
94+
cip.notify("Failed to convert info to YAML: "+err.Error(), 3*time.Second, "error")
95+
return
96+
}
97+
98+
cip.txtArea.SetText(string(yamlBytes), false)
99+
}
100+
101+
func (cip *ConsumerInfoPage) setupInputCapture() {
102+
cip.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
103+
if event.Key() == tcell.KeyEsc {
104+
cip.goBack()
105+
return nil
106+
}
107+
return event
108+
})
109+
}
110+
111+
func (cip *ConsumerInfoPage) goBack() {
112+
pages.SwitchToPage("consumerListPage")
113+
_, b := pages.GetFrontPage()
114+
b.(*ConsumerListPage).streamName = cip.streamName
115+
b.(*ConsumerListPage).redraw(&cip.Data.CurrCtx)
116+
cip.app.SetFocus(b)
117+
}
118+
119+
func (cip *ConsumerInfoPage) notify(message string, duration time.Duration, logLevel string) {
120+
cip.footerTxt.SetText(message)
121+
cip.footerTxt.SetTextColor(getLogLevelColor(logLevel))
122+
logger.Info(message)
123+
124+
go func() {
125+
time.Sleep(duration)
126+
cip.footerTxt.SetText("")
127+
cip.footerTxt.SetTextColor(tcell.ColorWhite)
128+
}()
129+
}

0 commit comments

Comments
 (0)