From 937af987e68288a1a41cc73a103a6edd64b6102e Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Thu, 23 Feb 2023 21:45:50 +1100 Subject: [PATCH] Removed the other tools and fixed the README.md file --- README.md | 30 +-- cmd/slog-view/main.go | 52 ---- cmd/sqs-browse/main.go | 94 ------- cmd/sqs-drain/main.go | 98 -------- cmd/ssm-browse/main.go | 67 ----- internal/slog-view/controllers/events.go | 7 - internal/slog-view/controllers/logfile.go | 47 ---- internal/slog-view/models/logfile.go | 10 - .../slog-view/services/logreader/logreader.go | 44 ---- internal/slog-view/styles/styles.go | 29 --- .../slog-view/ui/fullviewlinedetails/model.go | 67 ----- internal/slog-view/ui/linedetails/model.go | 77 ------ internal/slog-view/ui/loglines/events.go | 5 - internal/slog-view/ui/loglines/model.go | 97 -------- internal/slog-view/ui/loglines/tblmodel.go | 61 ----- internal/slog-view/ui/model.go | 83 ------- internal/sqs-browse/controllers/forward.go | 40 --- internal/sqs-browse/models/message.go | 11 - internal/sqs-browse/providers/sqs/provider.go | 78 ------ .../providers/stormstore/memstore.go | 31 --- .../sqs-browse/services/messages/iface.go | 11 - .../sqs-browse/services/messages/service.go | 26 -- .../sqs-browse/services/pollmessage/iface.go | 15 -- .../services/pollmessage/service.go | 46 ---- internal/sqs-browse/styles/styles.go | 29 --- internal/sqs-browse/ui/events.go | 5 - internal/sqs-browse/ui/model.go | 232 ------------------ internal/sqs-browse/ui/tblmodel.go | 27 -- internal/ssm-browse/controllers/events.go | 15 -- .../ssm-browse/controllers/ssmcontroller.go | 96 -------- internal/ssm-browse/models/models.go | 13 - .../ssm-browse/providers/awsssm/provider.go | 84 ------- .../services/ssmparameters/iface.go | 12 - .../services/ssmparameters/service.go | 33 --- internal/ssm-browse/styles/styles.go | 29 --- internal/ssm-browse/ui/model.go | 94 ------- internal/ssm-browse/ui/ssmdetails/model.go | 66 ----- internal/ssm-browse/ui/ssmlist/events.go | 5 - internal/ssm-browse/ui/ssmlist/ssmlist.go | 98 -------- internal/ssm-browse/ui/ssmlist/tblmodel.go | 24 -- test/cmd/load-test-ssm/main.go | 30 --- 41 files changed, 12 insertions(+), 2006 deletions(-) delete mode 100644 cmd/slog-view/main.go delete mode 100644 cmd/sqs-browse/main.go delete mode 100644 cmd/sqs-drain/main.go delete mode 100644 cmd/ssm-browse/main.go delete mode 100644 internal/slog-view/controllers/events.go delete mode 100644 internal/slog-view/controllers/logfile.go delete mode 100644 internal/slog-view/models/logfile.go delete mode 100644 internal/slog-view/services/logreader/logreader.go delete mode 100644 internal/slog-view/styles/styles.go delete mode 100644 internal/slog-view/ui/fullviewlinedetails/model.go delete mode 100644 internal/slog-view/ui/linedetails/model.go delete mode 100644 internal/slog-view/ui/loglines/events.go delete mode 100644 internal/slog-view/ui/loglines/model.go delete mode 100644 internal/slog-view/ui/loglines/tblmodel.go delete mode 100644 internal/slog-view/ui/model.go delete mode 100644 internal/sqs-browse/controllers/forward.go delete mode 100644 internal/sqs-browse/models/message.go delete mode 100644 internal/sqs-browse/providers/sqs/provider.go delete mode 100644 internal/sqs-browse/providers/stormstore/memstore.go delete mode 100644 internal/sqs-browse/services/messages/iface.go delete mode 100644 internal/sqs-browse/services/messages/service.go delete mode 100644 internal/sqs-browse/services/pollmessage/iface.go delete mode 100644 internal/sqs-browse/services/pollmessage/service.go delete mode 100644 internal/sqs-browse/styles/styles.go delete mode 100644 internal/sqs-browse/ui/events.go delete mode 100644 internal/sqs-browse/ui/model.go delete mode 100644 internal/sqs-browse/ui/tblmodel.go delete mode 100644 internal/ssm-browse/controllers/events.go delete mode 100644 internal/ssm-browse/controllers/ssmcontroller.go delete mode 100644 internal/ssm-browse/models/models.go delete mode 100644 internal/ssm-browse/providers/awsssm/provider.go delete mode 100644 internal/ssm-browse/services/ssmparameters/iface.go delete mode 100644 internal/ssm-browse/services/ssmparameters/service.go delete mode 100644 internal/ssm-browse/styles/styles.go delete mode 100644 internal/ssm-browse/ui/model.go delete mode 100644 internal/ssm-browse/ui/ssmdetails/model.go delete mode 100644 internal/ssm-browse/ui/ssmlist/events.go delete mode 100644 internal/ssm-browse/ui/ssmlist/ssmlist.go delete mode 100644 internal/ssm-browse/ui/ssmlist/tblmodel.go delete mode 100644 test/cmd/load-test-ssm/main.go diff --git a/README.md b/README.md index 7319afd..69fe711 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,25 @@ -# Audax Toolset +# Dynamo-Browse -A set of small, terminal based UI (TUI, sometimes called "Rougelike") tools for -administering AWS services. +A CLI tool for browsing DynamoDB tables. -They were built to make it easy to do quick things with -common AWS services, such as DynamoDB, without having to learn incantations with the CLI or -go to the AWS console itself. This keeps you focused on your task and saves you from -breaking concentration, especially if you do a lot in the terminal. +![dynamo-browse](https://dynamobrowse.app/images/dynamo-browse/main-item-view.png) -![dynamo-browse](https://audax.tools/images/dynamo-browse/main-item-view.png) - -## The Toolset - -More info about the available tools are available here: - -- [dynamo-browse](https://audax.tools/dynamo-browse): Browse DynamoDB tables +This was built to make it easy to quickly view and lightly edit +DynamoDB tables, running locally or within AWS, from the Terminal +without having to learn incantations with the CLI or +go to the AWS console itself. This helps from unnecessary context switching +if you tend to use the terminal a lot. ## Install -Binary packages can be [download from the release page](https://github.com/lmika/audax/releases/latest). +Binary packages can be [download from the release page](https://github.com/lmika/dynamo-browse/releases/latest). -If you have Go 1.18, you can install using the following command: +If you have Go 1.18 or later, you can install using the following command: ``` -go install github.com/lmika/audax/cmd/dynamo-browse@v0.1.0 +go install github.com/lmika/dynamo-browse/cmd/dynamo-browse@v0.1.0 ``` ## License -Audax toolset is released under the MIT License. +Dynamo-Browse is released under the MIT License. diff --git a/cmd/slog-view/main.go b/cmd/slog-view/main.go deleted file mode 100644 index b866a3a..0000000 --- a/cmd/slog-view/main.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "flag" - "fmt" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/common/ui/commandctrl" - "github.com/lmika/audax/internal/common/ui/logging" - "github.com/lmika/audax/internal/slog-view/controllers" - "github.com/lmika/audax/internal/slog-view/services/logreader" - "github.com/lmika/audax/internal/slog-view/ui" - "github.com/lmika/gopkgs/cli" - "os" -) - -func main() { - var flagDebug = flag.String("debug", "", "file to log debug messages") - flag.Parse() - - if flag.NArg() == 0 { - cli.Fatal("usage: slog-view LOGFILE") - } - - // Pre-determine if layout has dark background. This prevents calls for creating a list to hang. - lipgloss.HasDarkBackground() - - closeFn := logging.EnableLogging(*flagDebug) - defer closeFn() - - service := logreader.NewService() - - ctrl := controllers.NewLogFileController(service, flag.Arg(0)) - - cmdController := commandctrl.NewCommandController(nil) - //cmdController.AddCommands(&commandctrl.CommandList{ - // Commands: map[string]commandctrl.Command{ - // "cd": func(args []string) tea.Cmd { - // return ctrl.ChangePrefix(args[0]) - // }, - // }, - //}) - - model := ui.NewModel(ctrl, cmdController) - - p := tea.NewProgram(model, tea.WithAltScreen()) - - if err := p.Start(); err != nil { - fmt.Printf("Alas, there's been an error: %v", err) - os.Exit(1) - } -} diff --git a/cmd/sqs-browse/main.go b/cmd/sqs-browse/main.go deleted file mode 100644 index 4a9ac4b..0000000 --- a/cmd/sqs-browse/main.go +++ /dev/null @@ -1,94 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "log" - "os" - - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/sqs" - tea "github.com/charmbracelet/bubbletea" - "github.com/lmika/audax/internal/common/ui/dispatcher" - "github.com/lmika/audax/internal/sqs-browse/controllers" - "github.com/lmika/audax/internal/sqs-browse/models" - sqsprovider "github.com/lmika/audax/internal/sqs-browse/providers/sqs" - "github.com/lmika/audax/internal/sqs-browse/providers/stormstore" - "github.com/lmika/audax/internal/sqs-browse/services/messages" - "github.com/lmika/audax/internal/sqs-browse/services/pollmessage" - "github.com/lmika/audax/internal/sqs-browse/ui" - "github.com/lmika/events" - "github.com/lmika/gopkgs/cli" -) - -func main() { - var flagQueue = flag.String("q", "", "queue to poll") - var flagTarget = flag.String("t", "", "target queue to push to") - flag.Parse() - - ctx := context.Background() - cfg, err := config.LoadDefaultConfig(ctx) - if err != nil { - cli.Fatalf("cannot load AWS config: %v", err) - } - sqsClient := sqs.NewFromConfig(cfg) - - bus := events.New() - - workspaceFile, err := os.CreateTemp("", "sqs-browse*.workspace") - if err != nil { - cli.Fatalf("cannot create workspace file: %v", err) - } - workspaceFile.Close() // We just need the filename - - msgStore, err := stormstore.NewStore(workspaceFile.Name()) - if err != nil { - cli.Fatalf("cannot open workspace: %v", err) - } - defer msgStore.Close() - - sqsProvider := sqsprovider.NewProvider(sqsClient) - - messageService := messages.NewService(sqsProvider) - pollService := pollmessage.NewService(msgStore, sqsProvider, *flagQueue, bus) - - msgSendingHandlers := controllers.NewMessageSendingController(messageService, *flagTarget) - - loopback := &msgLoopback{} - uiDispatcher := dispatcher.NewDispatcher(loopback) - - uiModel := ui.NewModel(uiDispatcher, msgSendingHandlers) - p := tea.NewProgram(uiModel, tea.WithAltScreen()) - loopback.program = p - - bus.On("new-messages", func(m []*models.Message) { p.Send(ui.NewMessagesEvent(m)) }) - - f, err := tea.LogToFile("debug.log", "debug") - if err != nil { - fmt.Println("fatal:", err) - os.Exit(1) - } - defer f.Close() - - log.Printf("workspace file: %v", workspaceFile.Name()) - - go func() { - if err := pollService.Poll(context.Background()); err != nil { - log.Printf("cannot start poller: %v", err) - } - }() - - if err := p.Start(); err != nil { - fmt.Printf("Alas, there's been an error: %v", err) - os.Exit(1) - } -} - -type msgLoopback struct { - program *tea.Program -} - -func (m *msgLoopback) Send(msg tea.Msg) { - m.program.Send(msg) -} diff --git a/cmd/sqs-drain/main.go b/cmd/sqs-drain/main.go deleted file mode 100644 index a2efb9d..0000000 --- a/cmd/sqs-drain/main.go +++ /dev/null @@ -1,98 +0,0 @@ -package main - -import ( - "context" - "flag" - "log" - "os" - "path/filepath" - "time" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/sqs" - "github.com/aws/aws-sdk-go-v2/service/sqs/types" - "github.com/pkg/errors" - - "github.com/lmika/gopkgs/cli" -) - -func main() { - flagQueue := flag.String("q", "", "URL of queue to drain") - flagDir := flag.String("dir", "", "directory to save messages") - flag.Parse() - - if *flagQueue == "" { - cli.Fatalf("-q flag needs to be specified") - } - - ctx := context.Background() - cfg, err := config.LoadDefaultConfig(ctx) - if err != nil { - cli.Fatalf("cannot load AWS config: %v", err) - } - - outDir := *flagDir - if outDir == "" { - outDir = "out-" + time.Now().Format("20060102150405") - } - - if err := os.MkdirAll(outDir, 0755); err != nil { - cli.Fatalf("unable to create out dir: %v", err) - } - - client := sqs.NewFromConfig(cfg) - msgCount := 0 - for { - out, err := client.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{ - QueueUrl: aws.String(*flagQueue), - MaxNumberOfMessages: 10, - WaitTimeSeconds: 1, - }) - if err != nil { - log.Fatalf("error receiving messages: %v", err) - break - } else if len(out.Messages) == 0 { - break - } - - messagesToDelete := make([]types.DeleteMessageBatchRequestEntry, 0, 10) - for _, msg := range out.Messages { - if err := handleMessage(ctx, outDir, msg); err == nil { - messagesToDelete = append(messagesToDelete, types.DeleteMessageBatchRequestEntry{ - Id: msg.MessageId, - ReceiptHandle: msg.ReceiptHandle, - }) - msgCount += 1 - } else { - log.Println(err) - } - } - if len(messagesToDelete) == 0 { - log.Printf("no messages handled, terminating") - break - } - - if _, err := client.DeleteMessageBatch(ctx, &sqs.DeleteMessageBatchInput{ - QueueUrl: aws.String(*flagQueue), - Entries: messagesToDelete, - }); err != nil { - log.Printf("error deleting messages from queue: %v", err) - break - } - } - - log.Printf("Handled %v messages", msgCount) -} - -func handleMessage(ctx context.Context, outDir string, msg types.Message) error { - outFile := filepath.Join(outDir, aws.ToString(msg.MessageId)+".json") - msgBody := aws.ToString(msg.Body) - - log.Printf("%v -> %v", aws.ToString(msg.MessageId), outFile) - if err := os.WriteFile(outFile, []byte(msgBody), 0644); err != nil { - return errors.Wrapf(err, "unable to write message %v to file %v", msg.MessageId, outFile) - } - - return nil -} diff --git a/cmd/ssm-browse/main.go b/cmd/ssm-browse/main.go deleted file mode 100644 index ca7586d..0000000 --- a/cmd/ssm-browse/main.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/ssm" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/common/ui/commandctrl" - "github.com/lmika/audax/internal/common/ui/logging" - "github.com/lmika/audax/internal/ssm-browse/controllers" - "github.com/lmika/audax/internal/ssm-browse/providers/awsssm" - "github.com/lmika/audax/internal/ssm-browse/services/ssmparameters" - "github.com/lmika/audax/internal/ssm-browse/ui" - "github.com/lmika/gopkgs/cli" - "os" -) - -func main() { - var flagLocal = flag.Bool("local", false, "local endpoint") - var flagDebug = flag.String("debug", "", "file to log debug messages") - flag.Parse() - - // Pre-determine if layout has dark background. This prevents calls for creating a list to hang. - lipgloss.HasDarkBackground() - - closeFn := logging.EnableLogging(*flagDebug) - defer closeFn() - - cfg, err := config.LoadDefaultConfig(context.Background()) - if err != nil { - cli.Fatalf("cannot load AWS config: %v", err) - } - - var ssmClient *ssm.Client - if *flagLocal { - ssmClient = ssm.NewFromConfig(cfg, - ssm.WithEndpointResolver(ssm.EndpointResolverFromURL("http://localhost:4566"))) - } else { - ssmClient = ssm.NewFromConfig(cfg) - } - - provider := awsssm.NewProvider(ssmClient) - service := ssmparameters.NewService(provider) - - ctrl := controllers.New(service) - - cmdController := commandctrl.NewCommandController(nil) - cmdController.AddCommands(&commandctrl.CommandList{ - Commands: map[string]commandctrl.Command{ - "cd": func(ec commandctrl.ExecContext, args []string) tea.Msg { - return ctrl.ChangePrefix(args[0]) - }, - }, - }) - - model := ui.NewModel(ctrl, cmdController) - - p := tea.NewProgram(model, tea.WithAltScreen()) - - if err := p.Start(); err != nil { - fmt.Printf("Alas, there's been an error: %v", err) - os.Exit(1) - } -} diff --git a/internal/slog-view/controllers/events.go b/internal/slog-view/controllers/events.go deleted file mode 100644 index 83142fc..0000000 --- a/internal/slog-view/controllers/events.go +++ /dev/null @@ -1,7 +0,0 @@ -package controllers - -import "github.com/lmika/audax/internal/slog-view/models" - -type NewLogFile *models.LogFile - -type ViewLogLineFullScreen *models.LogLine \ No newline at end of file diff --git a/internal/slog-view/controllers/logfile.go b/internal/slog-view/controllers/logfile.go deleted file mode 100644 index f953597..0000000 --- a/internal/slog-view/controllers/logfile.go +++ /dev/null @@ -1,47 +0,0 @@ -package controllers - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/lmika/audax/internal/common/ui/events" - "github.com/lmika/audax/internal/slog-view/models" - "github.com/lmika/audax/internal/slog-view/services/logreader" - "sync" -) - -type LogFileController struct { - logReader *logreader.Service - - // state - mutex *sync.Mutex - filename string - logFile *models.LogFile -} - -func NewLogFileController(logReader *logreader.Service, filename string) *LogFileController { - return &LogFileController{ - logReader: logReader, - filename: filename, - mutex: new(sync.Mutex), - } -} - -func (lfc *LogFileController) ReadLogFile() tea.Cmd { - return func() tea.Msg { - logFile, err := lfc.logReader.Open(lfc.filename) - if err != nil { - return events.Error(err) - } - - return NewLogFile(logFile) - } -} - -func (lfc *LogFileController) ViewLogLineFullScreen(line *models.LogLine) tea.Cmd { - if line == nil { - return nil - } - - return func() tea.Msg { - return ViewLogLineFullScreen(line) - } -} diff --git a/internal/slog-view/models/logfile.go b/internal/slog-view/models/logfile.go deleted file mode 100644 index 700da71..0000000 --- a/internal/slog-view/models/logfile.go +++ /dev/null @@ -1,10 +0,0 @@ -package models - -type LogFile struct { - Filename string - Lines []LogLine -} - -type LogLine struct { - JSON interface{} -} diff --git a/internal/slog-view/services/logreader/logreader.go b/internal/slog-view/services/logreader/logreader.go deleted file mode 100644 index 9822e84..0000000 --- a/internal/slog-view/services/logreader/logreader.go +++ /dev/null @@ -1,44 +0,0 @@ -package logreader - -import ( - "bufio" - "encoding/json" - "github.com/lmika/audax/internal/slog-view/models" - "github.com/pkg/errors" - "log" - "os" -) - -type Service struct { -} - -func NewService() *Service { - return &Service{} -} - -func (s *Service) Open(filename string) (*models.LogFile, error) { - f, err := os.Open(filename) - if err != nil { - return nil, errors.Wrapf(err, "cannot open file: %v", filename) - } - defer f.Close() - - var lines []models.LogLine - scanner := bufio.NewScanner(f) - for scanner.Scan() { - line := scanner.Text() - - var data interface{} - if err := json.Unmarshal([]byte(line), &data); err != nil { - log.Printf("invalid json line: %v", err) - continue - } - - lines = append(lines, models.LogLine{JSON: data}) - } - if scanner.Err() != nil { - return nil, errors.Wrapf(err, "unable to scan file: %v", filename) - } - - return &models.LogFile{Lines: lines}, nil -} diff --git a/internal/slog-view/styles/styles.go b/internal/slog-view/styles/styles.go deleted file mode 100644 index ffd285c..0000000 --- a/internal/slog-view/styles/styles.go +++ /dev/null @@ -1,29 +0,0 @@ -package styles - -import ( - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/statusandprompt" -) - -type Styles struct { - Frames frame.Style - StatusAndPrompt statusandprompt.Style -} - -var DefaultStyles = Styles{ - Frames: frame.Style{ - ActiveTitle: lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")), - InactiveTitle: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")), - }, - StatusAndPrompt: statusandprompt.Style{ - ModeLine: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")), - }, -} diff --git a/internal/slog-view/ui/fullviewlinedetails/model.go b/internal/slog-view/ui/fullviewlinedetails/model.go deleted file mode 100644 index facf105..0000000 --- a/internal/slog-view/ui/fullviewlinedetails/model.go +++ /dev/null @@ -1,67 +0,0 @@ -package fullviewlinedetails - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" - "github.com/lmika/audax/internal/slog-view/models" - "github.com/lmika/audax/internal/slog-view/ui/linedetails" -) - -type Model struct { - submodel tea.Model - lineDetails *linedetails.Model - - visible bool -} - -func NewModel(submodel tea.Model, style frame.Style) *Model { - return &Model{ - submodel: submodel, - lineDetails: linedetails.New(style), - } -} - -func (*Model) Init() tea.Cmd { - return nil -} - -func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "esc": - m.visible = false - return m, nil - } - - if m.visible { - newModel, cmd := m.lineDetails.Update(msg) - m.lineDetails = newModel.(*linedetails.Model) - return m, cmd - } - } - - var cmd tea.Cmd - m.submodel, cmd = m.submodel.Update(msg) - return m, cmd -} - -func (m *Model) ViewItem(item *models.LogLine) { - m.visible = true - m.lineDetails.SetSelectedItem(item) - m.lineDetails.SetFocused(true) -} - -func (m *Model) View() string { - if m.visible { - return m.lineDetails.View() - } - return m.submodel.View() -} - -func (m *Model) Resize(w, h int) layout.ResizingModel { - m.submodel = layout.Resize(m.submodel, w, h) - m.lineDetails = layout.Resize(m.lineDetails, w, h).(*linedetails.Model) - return m -} diff --git a/internal/slog-view/ui/linedetails/model.go b/internal/slog-view/ui/linedetails/model.go deleted file mode 100644 index b6dd941..0000000 --- a/internal/slog-view/ui/linedetails/model.go +++ /dev/null @@ -1,77 +0,0 @@ -package linedetails - -import ( - "encoding/json" - "github.com/charmbracelet/bubbles/viewport" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" - "github.com/lmika/audax/internal/slog-view/models" -) - -type Model struct { - frameTitle frame.FrameTitle - viewport viewport.Model - w, h int - - // model state - focused bool - selectedItem *models.LogLine -} - -func New(style frame.Style) *Model { - viewport := viewport.New(0, 0) - viewport.SetContent("") - return &Model{ - frameTitle: frame.NewFrameTitle("Item", false, style), - viewport: viewport, - } -} - -func (*Model) Init() tea.Cmd { - return nil -} - -func (m *Model) SetFocused(newFocused bool) { - m.focused = newFocused -} - -func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - if m.focused { - newModel, cmd := m.viewport.Update(msg) - m.viewport = newModel - return m, cmd - } - } - - return m, nil -} - -func (m *Model) SetSelectedItem(item *models.LogLine) { - m.selectedItem = item - - if m.selectedItem != nil { - if formattedJson, err := json.MarshalIndent(item.JSON, "", " "); err == nil { - m.viewport.SetContent(string(formattedJson)) - } else { - m.viewport.SetContent("(not json)") - } - } else { - m.viewport.SetContent("(no line)") - } -} - -func (m *Model) View() string { - return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.viewport.View()) -} - -func (m *Model) Resize(w, h int) layout.ResizingModel { - m.w, m.h = w, h - m.frameTitle.Resize(w, h) - m.viewport.Width = w - m.viewport.Height = h - m.frameTitle.HeaderHeight() - return m -} diff --git a/internal/slog-view/ui/loglines/events.go b/internal/slog-view/ui/loglines/events.go deleted file mode 100644 index 8e7dabc..0000000 --- a/internal/slog-view/ui/loglines/events.go +++ /dev/null @@ -1,5 +0,0 @@ -package loglines - -import "github.com/lmika/audax/internal/slog-view/models" - -type NewLogLineSelected *models.LogLine diff --git a/internal/slog-view/ui/loglines/model.go b/internal/slog-view/ui/loglines/model.go deleted file mode 100644 index b21ed09..0000000 --- a/internal/slog-view/ui/loglines/model.go +++ /dev/null @@ -1,97 +0,0 @@ -package loglines - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" - "github.com/lmika/audax/internal/slog-view/models" - table "github.com/lmika/go-bubble-table" - "path/filepath" -) - -type Model struct { - frameTitle frame.FrameTitle - table table.Model - - logFile *models.LogFile - - w, h int -} - -func New(style frame.Style) *Model { - frameTitle := frame.NewFrameTitle("File: ", true, style) - table := table.New(table.SimpleColumns{"level", "error", "message"}, 0, 0) - - return &Model{ - frameTitle: frameTitle, - table: table, - } -} - -func (m *Model) SetLogFile(newLogFile *models.LogFile) { - m.logFile = newLogFile - m.frameTitle.SetTitle("File: " + filepath.Base(newLogFile.Filename)) - - cols := table.SimpleColumns{"level", "error", "message"} - - newTbl := table.New(cols, m.w, m.h-m.frameTitle.HeaderHeight()) - newRows := make([]table.Row, len(newLogFile.Lines)) - for i, r := range newLogFile.Lines { - newRows[i] = itemTableRow{r} - } - newTbl.SetRows(newRows) - - m.table = newTbl -} - -func (m *Model) Init() tea.Cmd { - return nil -} - -func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - //var cmd tea.Cmd - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "i", "up": - m.table.GoUp() - return m, m.emitNewSelectedParameter() - case "k", "down": - m.table.GoDown() - return m, m.emitNewSelectedParameter() - } - //m.table, cmd = m.table.Update(msg) - //return m, cmd - } - return m, nil -} - -func (m *Model) SelectedLogLine() *models.LogLine { - if row, ok := m.table.SelectedRow().(itemTableRow); ok { - return &(row.item) - } - return nil -} - -func (m *Model) emitNewSelectedParameter() tea.Cmd { - return func() tea.Msg { - selectedLogLine := m.SelectedLogLine() - if selectedLogLine != nil { - return NewLogLineSelected(selectedLogLine) - } - - return nil - } -} - -func (m *Model) View() string { - return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.table.View()) -} - -func (m *Model) Resize(w, h int) layout.ResizingModel { - m.w, m.h = w, h - m.frameTitle.Resize(w, h) - m.table.SetSize(w, h-m.frameTitle.HeaderHeight()) - return m -} diff --git a/internal/slog-view/ui/loglines/tblmodel.go b/internal/slog-view/ui/loglines/tblmodel.go deleted file mode 100644 index 0c7897a..0000000 --- a/internal/slog-view/ui/loglines/tblmodel.go +++ /dev/null @@ -1,61 +0,0 @@ -package loglines - -import ( - "fmt" - table "github.com/lmika/go-bubble-table" - "github.com/lmika/audax/internal/slog-view/models" - "io" - "strings" -) - -type itemTableRow struct { - item models.LogLine -} - -func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) { - // TODO: these cols are fixed, they should be dynamic - level := mtr.renderFirstLineOfField(mtr.item.JSON, "level") - err := mtr.renderFirstLineOfField(mtr.item.JSON, "error") - msg := mtr.renderFirstLineOfField(mtr.item.JSON, "message") - line := fmt.Sprintf("%s\t%s\t%s", level, err, msg) - - if index == model.Cursor() { - fmt.Fprintln(w, model.Styles.SelectedRow.Render(line)) - } else { - fmt.Fprintln(w, line) - } -} - -// TODO: this needs to be some form of path expression -func (mtr itemTableRow) renderFirstLineOfField(d interface{}, field string) string { - switch k := d.(type) { - case map[string]interface{}: - return mtr.renderFirstLineOfValue(k[field]) - default: - return mtr.renderFirstLineOfValue(k) - } -} - -func (mtr itemTableRow) renderFirstLineOfValue(v interface{}) string { - if v == nil { - return "" - } - - switch k := v.(type) { - case string: - firstLine := strings.SplitN(k, "\n", 2)[0] - return firstLine - case int: - return fmt.Sprint(k) - case float64: - return fmt.Sprint(k) - case bool: - return fmt.Sprint(k) - case map[string]interface{}: - return "{}" - case []interface{}: - return "[]" - default: - return "(other)" - } -} \ No newline at end of file diff --git a/internal/slog-view/ui/model.go b/internal/slog-view/ui/model.go deleted file mode 100644 index e153bbd..0000000 --- a/internal/slog-view/ui/model.go +++ /dev/null @@ -1,83 +0,0 @@ -package ui - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/lmika/audax/internal/common/ui/commandctrl" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/statusandprompt" - "github.com/lmika/audax/internal/slog-view/controllers" - "github.com/lmika/audax/internal/slog-view/styles" - "github.com/lmika/audax/internal/slog-view/ui/fullviewlinedetails" - "github.com/lmika/audax/internal/slog-view/ui/linedetails" - "github.com/lmika/audax/internal/slog-view/ui/loglines" -) - -type Model struct { - controller *controllers.LogFileController - cmdController *commandctrl.CommandController - - root tea.Model - logLines *loglines.Model - lineDetails *linedetails.Model - statusAndPrompt *statusandprompt.StatusAndPrompt - fullViewLineDetails *fullviewlinedetails.Model -} - -func NewModel(controller *controllers.LogFileController, cmdController *commandctrl.CommandController) Model { - defaultStyles := styles.DefaultStyles - logLines := loglines.New(defaultStyles.Frames) - lineDetails := linedetails.New(defaultStyles.Frames) - box := layout.NewVBox(layout.LastChildFixedAt(17), logLines, lineDetails) - fullViewLineDetails := fullviewlinedetails.NewModel(box, defaultStyles.Frames) - statusAndPrompt := statusandprompt.New(fullViewLineDetails, "", defaultStyles.StatusAndPrompt) - - root := layout.FullScreen(statusAndPrompt) - - return Model{ - controller: controller, - cmdController: cmdController, - root: root, - statusAndPrompt: statusAndPrompt, - logLines: logLines, - lineDetails: lineDetails, - fullViewLineDetails: fullViewLineDetails, - } -} - -func (m Model) Init() tea.Cmd { - return m.controller.ReadLogFile() -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case controllers.NewLogFile: - m.logLines.SetLogFile(msg) - case controllers.ViewLogLineFullScreen: - m.fullViewLineDetails.ViewItem(msg) - case loglines.NewLogLineSelected: - m.lineDetails.SetSelectedItem(msg) - - case tea.KeyMsg: - if !m.statusAndPrompt.InPrompt() { - switch msg.String() { - // TEMP - case ":": - return m, func() tea.Msg { return m.cmdController.Prompt() } - case "w": - return m, m.controller.ViewLogLineFullScreen(m.logLines.SelectedLogLine()) - // END TEMP - - case "ctrl+c", "q": - return m, tea.Quit - } - } - } - - newRoot, cmd := m.root.Update(msg) - m.root = newRoot - return m, cmd -} - -func (m Model) View() string { - return m.root.View() -} diff --git a/internal/sqs-browse/controllers/forward.go b/internal/sqs-browse/controllers/forward.go deleted file mode 100644 index d1fb509..0000000 --- a/internal/sqs-browse/controllers/forward.go +++ /dev/null @@ -1,40 +0,0 @@ -package controllers - -import ( - "context" - - "github.com/lmika/audax/internal/common/ui/uimodels" - "github.com/lmika/audax/internal/sqs-browse/models" - "github.com/lmika/audax/internal/sqs-browse/services/messages" - "github.com/pkg/errors" -) - -type MessageSendingController struct { - messageService *messages.Service - targetQueue string -} - -func NewMessageSendingController(messageService *messages.Service, targetQueue string) *MessageSendingController { - return &MessageSendingController{ - messageService: messageService, - targetQueue: targetQueue, - } -} - -func (msh *MessageSendingController) ForwardMessage(message models.Message) uimodels.Operation { - return uimodels.OperationFn(func(ctx context.Context) error { - uiCtx := uimodels.Ctx(ctx) - - if msh.targetQueue == "" { - return errors.New("target queue not set") - } - - messageId, err := msh.messageService.SendTo(ctx, message, msh.targetQueue) - if err != nil { - return errors.Wrapf(err, "cannot send message to %v", msh.targetQueue) - } - - uiCtx.Message("Message sent to " + msh.targetQueue + ", id = " + messageId) - return nil - }) -} diff --git a/internal/sqs-browse/models/message.go b/internal/sqs-browse/models/message.go deleted file mode 100644 index 7a22466..0000000 --- a/internal/sqs-browse/models/message.go +++ /dev/null @@ -1,11 +0,0 @@ -package models - -import "time" - -type Message struct { - ID uint64 `storm:"id,increment"` - ExtID string `storm:"unique"` - Queue string `storm:"index"` - Received time.Time - Data string -} diff --git a/internal/sqs-browse/providers/sqs/provider.go b/internal/sqs-browse/providers/sqs/provider.go deleted file mode 100644 index 17f8c20..0000000 --- a/internal/sqs-browse/providers/sqs/provider.go +++ /dev/null @@ -1,78 +0,0 @@ -package sqs - -import ( - "context" - "log" - "time" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/sqs" - "github.com/aws/aws-sdk-go-v2/service/sqs/types" - "github.com/lmika/audax/internal/sqs-browse/models" - "github.com/pkg/errors" -) - -type Provider struct { - client *sqs.Client -} - -func NewProvider(client *sqs.Client) *Provider { - return &Provider{client: client} -} - -func (p *Provider) SendMessage(ctx context.Context, msg models.Message, queue string) (string, error) { - // TEMP :: queue URL - - out, err := p.client.SendMessage(ctx, &sqs.SendMessageInput{ - QueueUrl: aws.String(queue), - MessageBody: aws.String(msg.Data), - }) - if err != nil { - return "", errors.Wrapf(err, "unable to send message to %v", queue) - } - - return aws.ToString(out.MessageId), nil -} - -func (p *Provider) PollForNewMessages(ctx context.Context, queue string) ([]*models.Message, error) { - out, err := p.client.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{ - QueueUrl: aws.String(queue), - MaxNumberOfMessages: 10, - WaitTimeSeconds: 20, - }) - if err != nil { - return nil, errors.Wrapf(err, "unable to receive messages from queue %v", queue) - } - - if len(out.Messages) == 0 { - return nil, nil - } - - messagesToReturn := make([]*models.Message, 0, len(out.Messages)) - messagesToDelete := make([]types.DeleteMessageBatchRequestEntry, 0, len(out.Messages)) - for _, msg := range out.Messages { - newLocalMessage := &models.Message{ - Queue: queue, - ExtID: aws.ToString(msg.MessageId), - Received: time.Now(), - Data: aws.ToString(msg.Body), - } - messagesToReturn = append(messagesToReturn, newLocalMessage) - - // Pull the message from the queue - // TODO: should this be determined by the caller? - messagesToDelete = append(messagesToDelete, types.DeleteMessageBatchRequestEntry{ - Id: msg.MessageId, - ReceiptHandle: msg.ReceiptHandle, - }) - } - - if _, err := p.client.DeleteMessageBatch(ctx, &sqs.DeleteMessageBatchInput{ - QueueUrl: aws.String(queue), - Entries: messagesToDelete, - }); err != nil { - log.Printf("error deleting messages from queue: %v", err) - } - - return messagesToReturn, nil -} diff --git a/internal/sqs-browse/providers/stormstore/memstore.go b/internal/sqs-browse/providers/stormstore/memstore.go deleted file mode 100644 index 45d7156..0000000 --- a/internal/sqs-browse/providers/stormstore/memstore.go +++ /dev/null @@ -1,31 +0,0 @@ -package stormstore - -import ( - "context" - - "github.com/asdine/storm" - "github.com/lmika/audax/internal/sqs-browse/models" - "github.com/pkg/errors" -) - -type Store struct { - db *storm.DB -} - -// TODO: should probably be a workspace provider -func NewStore(filename string) (*Store, error) { - db, err := storm.Open(filename) - if err != nil { - return nil, errors.Wrapf(err, "cannot open store %v", filename) - } - - return &Store{db: db}, nil -} - -func (s *Store) Close() { - s.db.Close() -} - -func (s *Store) Save(ctx context.Context, msg *models.Message) error { - return s.db.Save(msg) -} diff --git a/internal/sqs-browse/services/messages/iface.go b/internal/sqs-browse/services/messages/iface.go deleted file mode 100644 index daa4b6e..0000000 --- a/internal/sqs-browse/services/messages/iface.go +++ /dev/null @@ -1,11 +0,0 @@ -package messages - -import ( - "context" - - "github.com/lmika/audax/internal/sqs-browse/models" -) - -type MessageSender interface { - SendMessage(ctx context.Context, msg models.Message, queue string) (string, error) -} diff --git a/internal/sqs-browse/services/messages/service.go b/internal/sqs-browse/services/messages/service.go deleted file mode 100644 index 4ea0620..0000000 --- a/internal/sqs-browse/services/messages/service.go +++ /dev/null @@ -1,26 +0,0 @@ -package messages - -import ( - "context" - - "github.com/lmika/audax/internal/sqs-browse/models" - "github.com/pkg/errors" -) - -type Service struct { - messageSender MessageSender -} - -func NewService(messageSender MessageSender) *Service { - return &Service{ - messageSender: messageSender, - } -} - -func (s *Service) SendTo(ctx context.Context, msg models.Message, destQueue string) (string, error) { - messageId, err := s.messageSender.SendMessage(ctx, msg, destQueue) - if err != nil { - return "", errors.Wrapf(err, "cannot send message to %v", destQueue) - } - return messageId, nil -} diff --git a/internal/sqs-browse/services/pollmessage/iface.go b/internal/sqs-browse/services/pollmessage/iface.go deleted file mode 100644 index 4e17108..0000000 --- a/internal/sqs-browse/services/pollmessage/iface.go +++ /dev/null @@ -1,15 +0,0 @@ -package pollmessage - -import ( - "context" - - "github.com/lmika/audax/internal/sqs-browse/models" -) - -type MessageStore interface { - Save(ctx context.Context, msg *models.Message) error -} - -type MessagePoller interface { - PollForNewMessages(ctx context.Context, queue string) ([]*models.Message, error) -} diff --git a/internal/sqs-browse/services/pollmessage/service.go b/internal/sqs-browse/services/pollmessage/service.go deleted file mode 100644 index 69d2bef..0000000 --- a/internal/sqs-browse/services/pollmessage/service.go +++ /dev/null @@ -1,46 +0,0 @@ -package pollmessage - -import ( - "context" - "log" - - "github.com/lmika/events" - "github.com/pkg/errors" -) - -type Service struct { - store MessageStore - poller MessagePoller - queue string - bus *events.Bus -} - -func NewService(store MessageStore, poller MessagePoller, queue string, bus *events.Bus) *Service { - return &Service{ - store: store, - poller: poller, - queue: queue, - bus: bus, - } -} - -// Poll starts polling for new messages and adding them to the message store -func (s *Service) Poll(ctx context.Context) error { - for ctx.Err() == nil { - log.Printf("polling for new messages: %v", s.queue) - newMsgs, err := s.poller.PollForNewMessages(ctx, s.queue) - if err != nil { - return errors.Wrap(err, "unable to poll for messages") - } - - for _, msg := range newMsgs { - if err := s.store.Save(ctx, msg); err != nil { - log.Printf("warn: unable to save new message %v", err) - continue - } - } - - s.bus.Fire("new-messages", newMsgs) - } - return nil -} diff --git a/internal/sqs-browse/styles/styles.go b/internal/sqs-browse/styles/styles.go deleted file mode 100644 index 64f2460..0000000 --- a/internal/sqs-browse/styles/styles.go +++ /dev/null @@ -1,29 +0,0 @@ -package styles - -import ( - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/statusandprompt" -) - -type Styles struct { - Frames frame.Style - StatusAndPrompt statusandprompt.Style -} - -var DefaultStyles = Styles{ - Frames: frame.Style{ - ActiveTitle: lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#ffffff")). - Background(lipgloss.Color("#4479ff")), - InactiveTitle: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")), - }, - StatusAndPrompt: statusandprompt.Style{ - ModeLine: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")), - }, -} diff --git a/internal/sqs-browse/ui/events.go b/internal/sqs-browse/ui/events.go deleted file mode 100644 index a66d698..0000000 --- a/internal/sqs-browse/ui/events.go +++ /dev/null @@ -1,5 +0,0 @@ -package ui - -import "github.com/lmika/audax/internal/sqs-browse/models" - -type NewMessagesEvent []*models.Message diff --git a/internal/sqs-browse/ui/model.go b/internal/sqs-browse/ui/model.go deleted file mode 100644 index c95d8b3..0000000 --- a/internal/sqs-browse/ui/model.go +++ /dev/null @@ -1,232 +0,0 @@ -package ui - -import ( - "bytes" - "context" - "encoding/json" - "log" - "strings" - - "github.com/charmbracelet/bubbles/textinput" - "github.com/charmbracelet/bubbles/viewport" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/common/ui/dispatcher" - "github.com/lmika/audax/internal/common/ui/events" - "github.com/lmika/audax/internal/sqs-browse/controllers" - "github.com/lmika/audax/internal/sqs-browse/models" - table "github.com/lmika/go-bubble-table" -) - -var ( - activeHeaderStyle = lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#ffffff")). - Background(lipgloss.Color("#eac610")) - - inactiveHeaderStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")) -) - -type uiModel struct { - table table.Model - viewport viewport.Model - - ready bool - tableRows []table.Row - message string - - pendingInput *events.PromptForInputMsg - textInput textinput.Model - - dispatcher *dispatcher.Dispatcher - msgSendingHandlers *controllers.MessageSendingController -} - -func NewModel(dispatcher *dispatcher.Dispatcher, msgSendingHandlers *controllers.MessageSendingController) tea.Model { - tbl := table.New(table.SimpleColumns{"seq", "message"}, 100, 20) - rows := make([]table.Row, 0) - tbl.SetRows(rows) - - textInput := textinput.New() - - model := uiModel{ - table: tbl, - tableRows: rows, - message: "", - textInput: textInput, - msgSendingHandlers: msgSendingHandlers, - dispatcher: dispatcher, - } - - return model -} - -func (m uiModel) Init() tea.Cmd { - return nil -} - -func (m *uiModel) updateViewportToSelectedMessage() { - if message, ok := m.selectedMessage(); ok { - // TODO: not all messages are JSON - formattedJson := new(bytes.Buffer) - if err := json.Indent(formattedJson, []byte(message.Data), "", " "); err == nil { - m.viewport.SetContent(formattedJson.String()) - } else { - m.viewport.SetContent(message.Data) - } - } else { - m.viewport.SetContent("(no message selected)") - } -} - -func (m uiModel) selectedMessage() (models.Message, bool) { - if m.ready && len(m.tableRows) > 0 { - if message, ok := m.table.SelectedRow().(messageTableRow); ok { - return models.Message(message), true - } - } - return models.Message{}, false -} - -func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var textInputCommands tea.Cmd - - switch msg := msg.(type) { - // Shared messages - case events.ErrorMsg: - m.message = "Error: " + msg.Error() - case events.StatusMsg: - m.message = string(msg) - case events.PromptForInputMsg: - // TODO - //m.textInput.Focus() - //m.textInput.SetValue("") - //m.pendingInput = &msg - - // Local messages - case NewMessagesEvent: - for _, newMsg := range msg { - m.tableRows = append(m.tableRows, messageTableRow(*newMsg)) - } - m.table.SetRows(m.tableRows) - m.updateViewportToSelectedMessage() - - case tea.WindowSizeMsg: - fixedViewsHeight := lipgloss.Height(m.headerView()) + lipgloss.Height(m.splitterView()) + lipgloss.Height(m.footerView()) - - if !m.ready { - tableHeight := msg.Height / 2 - - m.table.SetSize(msg.Width, tableHeight) - m.viewport = viewport.New(msg.Width, msg.Height-tableHeight-fixedViewsHeight) - m.viewport.SetContent("(no message selected)") - m.ready = true - log.Println("Viewport is now ready") - } else { - tableHeight := msg.Height / 2 - - m.table.SetSize(msg.Width, tableHeight) - m.viewport.Width = msg.Width - m.viewport.Height = msg.Height - tableHeight - fixedViewsHeight - } - - m.textInput.Width = msg.Width - - m.textInput, textInputCommands = m.textInput.Update(msg) - case tea.KeyMsg: - - // If text input in focus, allow that to accept input messages - if m.pendingInput != nil { - switch msg.String() { - case "ctrl+c", "esc": - m.pendingInput = nil - case "enter": - //m.dispatcher.Start(uimodels.WithPromptValue(context.Background(), m.textInput.Value()), m.pendingInput.OnDone) - m.pendingInput = nil - default: - m.textInput, textInputCommands = m.textInput.Update(msg) - } - break - } - - // Normal focus - switch msg.String() { - - case "ctrl+c", "q": - return m, tea.Quit - case "up", "i": - m.table.GoUp() - m.updateViewportToSelectedMessage() - case "down", "k": - m.table.GoDown() - m.updateViewportToSelectedMessage() - - // TODO: these should be moved somewhere else - case "f": - if selectedMessage, ok := m.selectedMessage(); ok { - m.dispatcher.Start(context.Background(), m.msgSendingHandlers.ForwardMessage(selectedMessage)) - } - } - default: - m.textInput, textInputCommands = m.textInput.Update(msg) - } - - updatedTable, tableMsgs := m.table.Update(msg) - updatedViewport, viewportMsgs := m.viewport.Update(msg) - - m.table = updatedTable - m.viewport = updatedViewport - - return m, tea.Batch(textInputCommands, tableMsgs, viewportMsgs) -} - -func (m uiModel) View() string { - if !m.ready { - return "Initializing" - } - - if m.pendingInput != nil { - return lipgloss.JoinVertical(lipgloss.Top, - m.headerView(), - m.table.View(), - m.splitterView(), - m.viewport.View(), - m.textInput.View(), - ) - } - - return lipgloss.JoinVertical(lipgloss.Top, - m.headerView(), - m.table.View(), - m.splitterView(), - m.viewport.View(), - m.footerView(), - ) -} - -func (m uiModel) headerView() string { - title := activeHeaderStyle.Render("Queue: XXX") - line := activeHeaderStyle.Render(strings.Repeat(" ", max(0, m.viewport.Width-lipgloss.Width(title)))) - return lipgloss.JoinHorizontal(lipgloss.Left, title, line) -} - -func (m uiModel) splitterView() string { - title := inactiveHeaderStyle.Render("Message") - line := inactiveHeaderStyle.Render(strings.Repeat(" ", max(0, m.viewport.Width-lipgloss.Width(title)))) - return lipgloss.JoinHorizontal(lipgloss.Left, title, line) -} - -func (m uiModel) footerView() string { - title := m.message - line := strings.Repeat(" ", max(0, m.viewport.Width-lipgloss.Width(title))) - return lipgloss.JoinHorizontal(lipgloss.Left, title, line) -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} diff --git a/internal/sqs-browse/ui/tblmodel.go b/internal/sqs-browse/ui/tblmodel.go deleted file mode 100644 index b661db5..0000000 --- a/internal/sqs-browse/ui/tblmodel.go +++ /dev/null @@ -1,27 +0,0 @@ -package ui - -import ( - "fmt" - "io" - "strings" - - table "github.com/lmika/go-bubble-table" - "github.com/lmika/audax/internal/sqs-browse/models" -) - -type messageTableRow models.Message - -func (mtr messageTableRow) Render(w io.Writer, model table.Model, index int) { - firstLine := strings.SplitN(string(mtr.Data), "\n", 2)[0] - - sb := strings.Builder{} - sb.WriteString(fmt.Sprintf("%d", mtr.ID)) - sb.WriteString("\t") - sb.WriteString(firstLine) - - if index == model.Cursor() { - fmt.Fprintln(w, model.Styles.SelectedRow.Render(sb.String())) - } else { - fmt.Fprintln(w, sb.String()) - } -} diff --git a/internal/ssm-browse/controllers/events.go b/internal/ssm-browse/controllers/events.go deleted file mode 100644 index 11e3b9f..0000000 --- a/internal/ssm-browse/controllers/events.go +++ /dev/null @@ -1,15 +0,0 @@ -package controllers - -import ( - "fmt" - "github.com/lmika/audax/internal/ssm-browse/models" -) - -type NewParameterListMsg struct { - Prefix string - Parameters *models.SSMParameters -} - -func (rs NewParameterListMsg) StatusMessage() string { - return fmt.Sprintf("%d items returned", len(rs.Parameters.Items)) -} \ No newline at end of file diff --git a/internal/ssm-browse/controllers/ssmcontroller.go b/internal/ssm-browse/controllers/ssmcontroller.go deleted file mode 100644 index 52a2bea..0000000 --- a/internal/ssm-browse/controllers/ssmcontroller.go +++ /dev/null @@ -1,96 +0,0 @@ -package controllers - -import ( - "context" - tea "github.com/charmbracelet/bubbletea" - "github.com/lmika/audax/internal/common/ui/events" - "github.com/lmika/audax/internal/ssm-browse/models" - "github.com/lmika/audax/internal/ssm-browse/services/ssmparameters" - "sync" -) - -type SSMController struct { - service *ssmparameters.Service - - // state - mutex *sync.Mutex - prefix string -} - -func New(service *ssmparameters.Service) *SSMController { - return &SSMController{ - service: service, - prefix: "/", - mutex: new(sync.Mutex), - } -} - -func (c *SSMController) Fetch() tea.Cmd { - return func() tea.Msg { - res, err := c.service.List(context.Background(), c.prefix) - if err != nil { - return events.Error(err) - } - - return NewParameterListMsg{ - Prefix: c.prefix, - Parameters: res, - } - } -} - -func (c *SSMController) ChangePrefix(newPrefix string) tea.Msg { - res, err := c.service.List(context.Background(), newPrefix) - if err != nil { - return events.Error(err) - } - - c.mutex.Lock() - defer c.mutex.Unlock() - c.prefix = newPrefix - - return NewParameterListMsg{ - Prefix: c.prefix, - Parameters: res, - } -} - -func (c *SSMController) Clone(param models.SSMParameter) tea.Msg { - return events.PromptForInput("New key: ", nil, func(value string) tea.Msg { - return func() tea.Msg { - ctx := context.Background() - if err := c.service.Clone(ctx, param, value); err != nil { - return events.Error(err) - } - - res, err := c.service.List(context.Background(), c.prefix) - if err != nil { - return events.Error(err) - } - - return NewParameterListMsg{ - Prefix: c.prefix, - Parameters: res, - } - } - }) -} - -func (c *SSMController) DeleteParameter(param models.SSMParameter) tea.Msg { - return events.ConfirmYes("delete parameter? ", func() tea.Msg { - ctx := context.Background() - if err := c.service.Delete(ctx, param); err != nil { - return events.Error(err) - } - - res, err := c.service.List(context.Background(), c.prefix) - if err != nil { - return events.Error(err) - } - - return NewParameterListMsg{ - Prefix: c.prefix, - Parameters: res, - } - }) -} diff --git a/internal/ssm-browse/models/models.go b/internal/ssm-browse/models/models.go deleted file mode 100644 index 74a9b7d..0000000 --- a/internal/ssm-browse/models/models.go +++ /dev/null @@ -1,13 +0,0 @@ -package models - -import "github.com/aws/aws-sdk-go-v2/service/ssm/types" - -type SSMParameters struct { - Items []SSMParameter -} - -type SSMParameter struct { - Name string - Type types.ParameterType - Value string -} diff --git a/internal/ssm-browse/providers/awsssm/provider.go b/internal/ssm-browse/providers/awsssm/provider.go deleted file mode 100644 index 5c03cd7..0000000 --- a/internal/ssm-browse/providers/awsssm/provider.go +++ /dev/null @@ -1,84 +0,0 @@ -package awsssm - -import ( - "context" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ssm" - "github.com/aws/aws-sdk-go-v2/service/ssm/types" - "github.com/lmika/audax/internal/ssm-browse/models" - "github.com/pkg/errors" - "log" -) - -const defaultKMSKeyIDForSecureStrings = "alias/aws/ssm" - -type Provider struct { - client *ssm.Client -} - -func NewProvider(client *ssm.Client) *Provider { - return &Provider{ - client: client, - } -} - -func (p *Provider) List(ctx context.Context, prefix string, maxCount int) (*models.SSMParameters, error) { - log.Printf("new prefix: %v", prefix) - - pager := ssm.NewGetParametersByPathPaginator(p.client, &ssm.GetParametersByPathInput{ - Path: aws.String(prefix), - Recursive: true, - WithDecryption: true, - }) - - items := make([]models.SSMParameter, 0) -outer: - for pager.HasMorePages() { - out, err := pager.NextPage(ctx) - if err != nil { - return nil, errors.Wrap(err, "cannot get parameters from path") - } - - for _, p := range out.Parameters { - items = append(items, models.SSMParameter{ - Name: aws.ToString(p.Name), - Type: p.Type, - Value: aws.ToString(p.Value), - }) - if len(items) >= maxCount { - break outer - } - } - } - - return &models.SSMParameters{Items: items}, nil -} - -func (p *Provider) Put(ctx context.Context, param models.SSMParameter, override bool) error { - in := &ssm.PutParameterInput{ - Name: aws.String(param.Name), - Type: param.Type, - Value: aws.String(param.Value), - Overwrite: override, - } - if param.Type == types.ParameterTypeSecureString { - in.KeyId = aws.String(defaultKMSKeyIDForSecureStrings) - } - - _, err := p.client.PutParameter(ctx, in) - if err != nil { - return errors.Wrap(err, "unable to put new SSM parameter") - } - - return nil -} - -func (p *Provider) Delete(ctx context.Context, param models.SSMParameter) error { - _, err := p.client.DeleteParameter(ctx, &ssm.DeleteParameterInput{ - Name: aws.String(param.Name), - }) - if err != nil { - return errors.Wrap(err, "unable to delete SSM parameter") - } - return nil -} diff --git a/internal/ssm-browse/services/ssmparameters/iface.go b/internal/ssm-browse/services/ssmparameters/iface.go deleted file mode 100644 index b33b431..0000000 --- a/internal/ssm-browse/services/ssmparameters/iface.go +++ /dev/null @@ -1,12 +0,0 @@ -package ssmparameters - -import ( - "context" - "github.com/lmika/audax/internal/ssm-browse/models" -) - -type SSMProvider interface { - List(ctx context.Context, prefix string, maxCount int) (*models.SSMParameters, error) - Put(ctx context.Context, param models.SSMParameter, override bool) error - Delete(ctx context.Context, param models.SSMParameter) error -} diff --git a/internal/ssm-browse/services/ssmparameters/service.go b/internal/ssm-browse/services/ssmparameters/service.go deleted file mode 100644 index 613940a..0000000 --- a/internal/ssm-browse/services/ssmparameters/service.go +++ /dev/null @@ -1,33 +0,0 @@ -package ssmparameters - -import ( - "context" - "github.com/lmika/audax/internal/ssm-browse/models" -) - -type Service struct { - provider SSMProvider -} - -func NewService(provider SSMProvider) *Service { - return &Service{ - provider: provider, - } -} - -func (s *Service) List(ctx context.Context, prefix string) (*models.SSMParameters, error) { - return s.provider.List(ctx, prefix, 100) -} - -func (s *Service) Clone(ctx context.Context, param models.SSMParameter, newName string) error { - newParam := models.SSMParameter{ - Name: newName, - Type: param.Type, - Value: param.Value, - } - return s.provider.Put(ctx, newParam, false) -} - -func (s *Service) Delete(ctx context.Context, param models.SSMParameter) error { - return s.provider.Delete(ctx, param) -} diff --git a/internal/ssm-browse/styles/styles.go b/internal/ssm-browse/styles/styles.go deleted file mode 100644 index 5abdb68..0000000 --- a/internal/ssm-browse/styles/styles.go +++ /dev/null @@ -1,29 +0,0 @@ -package styles - -import ( - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/statusandprompt" -) - -type Styles struct { - Frames frame.Style - StatusAndPrompt statusandprompt.Style -} - -var DefaultStyles = Styles{ - Frames: frame.Style{ - ActiveTitle: lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#ffffff")). - Background(lipgloss.Color("#c144ff")), - InactiveTitle: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")), - }, - StatusAndPrompt: statusandprompt.Style{ - ModeLine: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#000000")). - Background(lipgloss.Color("#d1d1d1")), - }, -} diff --git a/internal/ssm-browse/ui/model.go b/internal/ssm-browse/ui/model.go deleted file mode 100644 index e178a70..0000000 --- a/internal/ssm-browse/ui/model.go +++ /dev/null @@ -1,94 +0,0 @@ -package ui - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/lmika/audax/internal/common/ui/commandctrl" - "github.com/lmika/audax/internal/common/ui/events" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/statusandprompt" - "github.com/lmika/audax/internal/ssm-browse/controllers" - "github.com/lmika/audax/internal/ssm-browse/styles" - "github.com/lmika/audax/internal/ssm-browse/ui/ssmdetails" - "github.com/lmika/audax/internal/ssm-browse/ui/ssmlist" - "github.com/pkg/errors" -) - -type Model struct { - cmdController *commandctrl.CommandController - controller *controllers.SSMController - statusAndPrompt *statusandprompt.StatusAndPrompt - - root tea.Model - ssmList *ssmlist.Model - ssmDetails *ssmdetails.Model -} - -func NewModel(controller *controllers.SSMController, cmdController *commandctrl.CommandController) Model { - defaultStyles := styles.DefaultStyles - ssmList := ssmlist.New(defaultStyles.Frames) - ssmdDetails := ssmdetails.New(defaultStyles.Frames) - statusAndPrompt := statusandprompt.New( - layout.NewVBox(layout.LastChildFixedAt(17), ssmList, ssmdDetails), "", defaultStyles.StatusAndPrompt) - - cmdController.AddCommands(&commandctrl.CommandList{ - Commands: map[string]commandctrl.Command{ - "clone": func(ec commandctrl.ExecContext, args []string) tea.Msg { - if currentParam := ssmList.CurrentParameter(); currentParam != nil { - return controller.Clone(*currentParam) - } - return events.Error(errors.New("no parameter selected")) - }, - "delete": func(ec commandctrl.ExecContext, args []string) tea.Msg { - if currentParam := ssmList.CurrentParameter(); currentParam != nil { - return controller.DeleteParameter(*currentParam) - } - return events.Error(errors.New("no parameter selected")) - }, - }, - }) - - root := layout.FullScreen(statusAndPrompt) - - return Model{ - controller: controller, - cmdController: cmdController, - root: root, - statusAndPrompt: statusAndPrompt, - ssmList: ssmList, - ssmDetails: ssmdDetails, - } -} - -func (m Model) Init() tea.Cmd { - return m.controller.Fetch() -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case controllers.NewParameterListMsg: - m.ssmList.SetPrefix(msg.Prefix) - m.ssmList.SetParameters(msg.Parameters) - case ssmlist.NewSSMParameterSelected: - m.ssmDetails.SetSelectedItem(msg) - case tea.KeyMsg: - if !m.statusAndPrompt.InPrompt() { - switch msg.String() { - // TEMP - case ":": - return m, func() tea.Msg { return m.cmdController.Prompt() } - // END TEMP - - case "ctrl+c", "q": - return m, tea.Quit - } - } - } - - newRoot, cmd := m.root.Update(msg) - m.root = newRoot - return m, cmd -} - -func (m Model) View() string { - return m.root.View() -} diff --git a/internal/ssm-browse/ui/ssmdetails/model.go b/internal/ssm-browse/ui/ssmdetails/model.go deleted file mode 100644 index a0b6a67..0000000 --- a/internal/ssm-browse/ui/ssmdetails/model.go +++ /dev/null @@ -1,66 +0,0 @@ -package ssmdetails - -import ( - "fmt" - "github.com/charmbracelet/bubbles/viewport" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" - "github.com/lmika/audax/internal/ssm-browse/models" - "strings" -) - -type Model struct { - frameTitle frame.FrameTitle - viewport viewport.Model - w, h int - - // model state - hasSelectedItem bool - selectedItem *models.SSMParameter -} - -func New(style frame.Style) *Model { - viewport := viewport.New(0, 0) - viewport.SetContent("") - return &Model{ - frameTitle: frame.NewFrameTitle("Item", false, style), - viewport: viewport, - } -} - -func (*Model) Init() tea.Cmd { - return nil -} - -func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - return m, nil -} - -func (m *Model) SetSelectedItem(item *models.SSMParameter) { - m.selectedItem = item - - if m.selectedItem != nil { - var viewportContents strings.Builder - fmt.Fprintf(&viewportContents, "Name: %v\n\n", item.Name) - fmt.Fprintf(&viewportContents, "Type: TODO\n\n") - fmt.Fprintf(&viewportContents, "%v\n", item.Value) - - m.viewport.SetContent(viewportContents.String()) - } else { - m.viewport.SetContent("(no parameter selected)") - } -} - -func (m *Model) View() string { - return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.viewport.View()) -} - -func (m *Model) Resize(w, h int) layout.ResizingModel { - m.w, m.h = w, h - m.frameTitle.Resize(w, h) - m.viewport.Width = w - m.viewport.Height = h - m.frameTitle.HeaderHeight() - return m -} diff --git a/internal/ssm-browse/ui/ssmlist/events.go b/internal/ssm-browse/ui/ssmlist/events.go deleted file mode 100644 index 95f20f3..0000000 --- a/internal/ssm-browse/ui/ssmlist/events.go +++ /dev/null @@ -1,5 +0,0 @@ -package ssmlist - -import "github.com/lmika/audax/internal/ssm-browse/models" - -type NewSSMParameterSelected *models.SSMParameter diff --git a/internal/ssm-browse/ui/ssmlist/ssmlist.go b/internal/ssm-browse/ui/ssmlist/ssmlist.go deleted file mode 100644 index 79a401d..0000000 --- a/internal/ssm-browse/ui/ssmlist/ssmlist.go +++ /dev/null @@ -1,98 +0,0 @@ -package ssmlist - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/frame" - "github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/layout" - "github.com/lmika/audax/internal/ssm-browse/models" - table "github.com/lmika/go-bubble-table" -) - -type Model struct { - frameTitle frame.FrameTitle - table table.Model - - parameters *models.SSMParameters - - w, h int -} - -func New(style frame.Style) *Model { - frameTitle := frame.NewFrameTitle("SSM: /", true, style) - table := table.New(table.SimpleColumns{"name", "type", "value"}, 0, 0) - - return &Model{ - frameTitle: frameTitle, - table: table, - } -} - -func (m *Model) SetPrefix(newPrefix string) { - m.frameTitle.SetTitle("SSM: " + newPrefix) -} - -func (m *Model) SetParameters(parameters *models.SSMParameters) { - m.parameters = parameters - cols := table.SimpleColumns{"name", "type", "value"} - - newTbl := table.New(cols, m.w, m.h-m.frameTitle.HeaderHeight()) - newRows := make([]table.Row, len(parameters.Items)) - for i, r := range parameters.Items { - newRows[i] = itemTableRow{r} - } - newTbl.SetRows(newRows) - - m.table = newTbl -} - -func (m *Model) Init() tea.Cmd { - return nil -} - -func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - //var cmd tea.Cmd - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "i", "up": - m.table.GoUp() - return m, m.emitNewSelectedParameter() - case "k", "down": - m.table.GoDown() - return m, m.emitNewSelectedParameter() - } - //m.table, cmd = m.table.Update(msg) - //return m, cmd - } - return m, nil -} - -func (m *Model) emitNewSelectedParameter() tea.Cmd { - return func() tea.Msg { - if row, ok := m.table.SelectedRow().(itemTableRow); ok { - return NewSSMParameterSelected(&(row.item)) - } - - return nil - } -} - -func (m *Model) CurrentParameter() *models.SSMParameter { - if row, ok := m.table.SelectedRow().(itemTableRow); ok { - return &(row.item) - } - - return nil -} - -func (m *Model) View() string { - return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.table.View()) -} - -func (m *Model) Resize(w, h int) layout.ResizingModel { - m.w, m.h = w, h - m.frameTitle.Resize(w, h) - m.table.SetSize(w, h-m.frameTitle.HeaderHeight()) - return m -} diff --git a/internal/ssm-browse/ui/ssmlist/tblmodel.go b/internal/ssm-browse/ui/ssmlist/tblmodel.go deleted file mode 100644 index 5851ac5..0000000 --- a/internal/ssm-browse/ui/ssmlist/tblmodel.go +++ /dev/null @@ -1,24 +0,0 @@ -package ssmlist - -import ( - "fmt" - table "github.com/lmika/go-bubble-table" - "github.com/lmika/audax/internal/ssm-browse/models" - "io" - "strings" -) - -type itemTableRow struct { - item models.SSMParameter -} - -func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) { - firstLine := strings.SplitN(mtr.item.Value, "\n", 2)[0] - line := fmt.Sprintf("%s\t%s\t%s", mtr.item.Name, mtr.item.Type, firstLine) - - if index == model.Cursor() { - fmt.Fprintln(w, model.Styles.SelectedRow.Render(line)) - } else { - fmt.Fprintln(w, line) - } -} diff --git a/test/cmd/load-test-ssm/main.go b/test/cmd/load-test-ssm/main.go deleted file mode 100644 index cf9df9f..0000000 --- a/test/cmd/load-test-ssm/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "context" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/ssm" - "github.com/aws/aws-sdk-go-v2/service/ssm/types" - "github.com/lmika/gopkgs/cli" -) - -func main() { - ctx := context.Background() - - cfg, err := config.LoadDefaultConfig(ctx) - if err != nil { - cli.Fatalf("cannot load AWS config: %v", err) - } - - ssmClient := ssm.NewFromConfig(cfg, - ssm.WithEndpointResolver(ssm.EndpointResolverFromURL("http://localhost:4566"))) - - if _, err := ssmClient.PutParameter(ctx, &ssm.PutParameterInput{ - Name: aws.String("/alpha/bravo"), - Type: types.ParameterTypeString, - Value: aws.String("This is a parameter value"), - }); err != nil { - cli.Fatal(err) - } -}