Skip to content

Commit

Permalink
feat: add support for persisting, improve parsing of msgs
Browse files Browse the repository at this point in the history
  • Loading branch information
dhth committed Mar 6, 2024
1 parent 71b078c commit ed3750c
Show file tree
Hide file tree
Showing 16 changed files with 423 additions and 94 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Go build

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22.0'

- name: Build
run: go build -v ./...
36 changes: 36 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Release

on:
push:
tags:
- 'v*'

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22.0'
- name: Build
run: go build -v ./...
- name: Install Cosign
uses: sigstore/cosign-installer@v3
with:
cosign-release: 'v2.2.3'
- name: Store Cosign private key in a file
run: 'echo "$COSIGN_KEY" > cosign.key'
shell: bash
env:
COSIGN_KEY: ${{secrets.COSIGN_KEY}}
- name: Release Binaries
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{secrets.GH_PAT}}
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ dist
cueitup
records
debug.log
messages
46 changes: 46 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
version: 1

before:
hooks:
- go mod tidy
- go generate ./...

builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- darwin
goarch:
- amd64
- arm64
signs:
- cmd: cosign
stdin: "{{ .Env.COSIGN_PASSWORD }}"
args:
- "sign-blob"
- "--key=cosign.key"
- "--output-signature=${signature}"
- "${artifact}"
- "--yes" # needed on cosign 2.0.0+
artifacts: all


brews:
- name: cueitup
repository:
owner: dhth
name: homebrew-tap
folder: Formula
license: MIT
homepage: "https://github.com/dhth/cueitup"
description: "Inspect messages in an AWS SQS queue in a simple and deliberate manner"

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^ci:"

21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Dhruv Thakur

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
98 changes: 97 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,105 @@
---

`cueitup` lets you inspect messages in an AWS SQS queue in a simple and
deliberate manner.
deliberate manner. It was built to simplify the process of investigating the
contents of messages being pushed to an SNS topic. You can pull one or more
messages on demand, peruse through them in a list, and, if needed, persist them
to your local filesystem.

<p align="center">
<img src="./assets/cueitup.gif?raw=true" alt="Usage" />
</p>

Install
---

**homebrew**:

```sh
brew install dhth/tap/cueitup
```

**go**:

```sh
go install github.com/dhth/cueitup@latest
```

⚡️ Usage
---

### Consuming JSON messages

#### Basic usage

```bash

cueitup \
-aws-profile="<PROFILE>" \
-aws-region="<REGION>" \
-queue-url="https://sqs.eu-central-1.amazonaws.com/<ABC>/<XYX>" \
-msg-format=json
```

#### Viewing a subset of the full payload

To only view the nested object with the key 'Message' in the JSON
payload below, use 👇

```json
{
"Type": "Notification",
"MessageId": "f7bbec51-1cd1-4630-8eb3-7b124de6d6f4",
"TopicArn": "arn:aws:sns:eu-central-1:123:queue-name",
"Message": {
"companyId": "af8e74b2-82db-4349-b861-c1d9d1a3033f",
"resourceId": "611a709e-2b96-41e3-9274-8bbd4e191334",
"aggregateId": "93422d4d-90ec-4a20-a794-3f835d7605cf",
"sequenceNr": 59,
"dateTime": "b5692ca5-e060-4318-8a40-e2b806a4018a",
"type": "com.some.kind.of.event",
"version": 1
},
"Timestamp": "5f0244d6-e640-43f4-86d0-5b9aa639c7df",
"SignatureVersion": "1",
"Signature": "XYZ",
"SigningCertURL": "https://sns.eu-central-1.amazonaws.com/SimpleNotificationService-ABC",
"UnsubscribeURL": "https://sns.eu-central-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-central-1:XYZ"
}
```

```bash
cueitup \
-aws-profile="<PROFILE>" \
-aws-region="<REGION>" \
-queue-url="https://sqs.eu-central-1.amazonaws.com/<ABC>/<XYX>" \
-msg-format='json' \
-subset-key='Message'
```

#### Adding Context to Your List

You can provide a key, whose value will be shown as for context in the list.

```bash
cueitup \
-aws-profile="<PROFILE>" \
-aws-region="<REGION>" \
-queue-url="https://sqs.eu-central-1.amazonaws.com/<ABC>/<XYX>" \
-msg-format='json' \
-subset-key='Message' \
-context-key='resourceId'

```

TODO
---

- [ ] Add ability to only save records with a chosen set of keys

Acknowledgements
---

`cueitup` is built using the awesome TUI framework [bubbletea][1].

[1]: https://github.com/charmbracelet/bubbletea
Binary file added assets/cueitup.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 32 additions & 7 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ import (
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/dhth/cueitup/ui"
"github.com/dhth/cueitup/ui/model"
)

func die(msg string, args ...any) {
fmt.Fprintf(os.Stderr, msg, args...)
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}

var (
queueUrl = flag.String("queue-url", "", "url of the queue to consume from")
awsProfile = flag.String("aws-profile", "", "aws profile to use")
awsRegion = flag.String("aws-region", "", "aws region to use")
extractJSONObject = flag.String("json-extract", "", "extract a nested object inside the JSON body")
keyProperty = flag.String("key-property", "", "the key to use as for context in the list")
queueUrl = flag.String("queue-url", "", "url of the queue to consume from")
awsProfile = flag.String("aws-profile", "", "aws profile to use")
awsRegion = flag.String("aws-region", "", "aws region to use")
msgFormat = flag.String("msg-format", "json", "message format")
subsetKey = flag.String("subset-key", "", "extract a nested object inside the JSON body")
contextKey = flag.String("context-key", "", "the key to use as for context in the list")
)

func Execute() {
Expand All @@ -42,6 +44,29 @@ func Execute() {
die("aws-region cannot be empty")
}

var msgFmt model.MsgFmt
switch *msgFormat {
case "json":
msgFmt = model.JsonFmt
case "plaintext":
msgFmt = model.PlainTxtFmt
default:
die("cueitup only supports the following msg-format values: json, plaintext")
}

if *subsetKey != "" && msgFmt != model.JsonFmt {
die("subset-key can only be used when msg-format=json")
}
if *contextKey != "" && msgFmt != model.JsonFmt {
die("context-key can only be used when msg-format=json")
}

msgConsumptionConf := model.MsgConsumptionConf{
Format: msgFmt,
SubsetKey: *subsetKey,
ContextKey: *contextKey,
}

sdkConfig, err := config.LoadDefaultConfig(context.TODO(),
config.WithSharedConfigProfile(*awsProfile),
config.WithRegion(*awsRegion),
Expand All @@ -53,6 +78,6 @@ func Execute() {

sqsClient := sqs.NewFromConfig(sdkConfig)

ui.RenderUI(sqsClient, *queueUrl, *extractJSONObject, *keyProperty)
ui.RenderUI(sqsClient, *queueUrl, msgConsumptionConf)

}
50 changes: 32 additions & 18 deletions ui/model/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package model
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"time"

Expand All @@ -12,7 +14,7 @@ import (
tea "github.com/charmbracelet/bubbletea"
)

func FetchMessages(client *sqs.Client, queueUrl string, maxMessages int32, waitTime int32, extractJSONObject string, keyProperty string) tea.Cmd {
func FetchMessages(client *sqs.Client, queueUrl string, maxMessages int32, waitTime int32, msgConsumptionConf MsgConsumptionConf) tea.Cmd {
return func() tea.Msg {

var messages []types.Message
Expand All @@ -35,7 +37,7 @@ func FetchMessages(client *sqs.Client, queueUrl string, maxMessages int32, waitT
} else {
messages = result.Messages
for _, message := range messages {
msgValue, keyValue, _ := getMessageData(&message, extractJSONObject, keyProperty)
msgValue, keyValue, _ := getMessageData(&message, msgConsumptionConf)
messagesValues = append(messagesValues, msgValue)
keyValues = append(keyValues, keyValue)
}
Expand Down Expand Up @@ -83,6 +85,13 @@ func GetQueueMsgCount(client *sqs.Client, queueUrl string) tea.Cmd {
AttributeNames: []types.QueueAttributeName{approxMsgCountType},
})

if err != nil {
return QueueMsgCountFetchedMsg{
approxMsgCount: -1,
err: err,
}
}

countStr := attribute.Attributes[string(approxMsgCountType)]
count, err := strconv.Atoi(countStr)
if err != nil {
Expand All @@ -97,22 +106,27 @@ func GetQueueMsgCount(client *sqs.Client, queueUrl string) tea.Cmd {
}
}

// func saveRecordValue(message *types.Message, extractJSONObject string) tea.Cmd {
// return func() tea.Msg {
// var msgValue string
// var err error
// if extractJSONObject != "" {
// msgValue, err = getRecordValueJSON(message, extractJSONObject)
// } else {
// msgValue, err = getRecordValueJSONFull(message)
// }
// if err != nil {
// return KMsgValueReadyMsg{err: err}
// } else {
// return KMsgValueReadyMsg{storeKey: *message.MessageId, record: message, msgValue: msgValue}
// }
// }
// }
func saveRecordValueToDisk(filePath string, msgValue string, msgFmt MsgFmt) tea.Cmd {
return func() tea.Msg {
dir := filepath.Dir(filePath)
err := os.MkdirAll(dir, 0755)
if err != nil {
return RecordSavedToDiskMsg{err: err}
}
var data string
switch msgFmt {
case JsonFmt:
data = fmt.Sprintf("```json\n%s\n```", msgValue)
case PlainTxtFmt:
data = msgValue
}
err = os.WriteFile(filePath, []byte(data), 0644)
if err != nil {
return RecordSavedToDiskMsg{err: err}
}
return RecordSavedToDiskMsg{path: filePath}
}
}

func showItemDetails(key string) tea.Cmd {
return func() tea.Msg {
Expand Down

0 comments on commit ed3750c

Please sign in to comment.