Skip to content

Commit

Permalink
first implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
winebarrel committed Jan 5, 2024
1 parent 9e93b81 commit af94bbd
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 1 deletion.
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
@@ -0,0 +1,26 @@
name: CI
on:
push:
branches:
- main
pull_request:

defaults:
run:
shell: bash -xe {0}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
cache: false
- uses: golangci/golangci-lint-action@v3
- run: make
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -19,3 +19,5 @@

# Go workspace file
go.work

/las
6 changes: 6 additions & 0 deletions .golangci.yml
@@ -0,0 +1,6 @@
linters:
enable:
- misspell
- gosimple
run:
timeout: 5m
14 changes: 14 additions & 0 deletions Makefile
@@ -0,0 +1,14 @@
.PHONY: all
all: vet build

.PHONY: build
build:
go build ./cmd/las

.PHONY: vet
vet:
go vet ./...

.PHONY: lint
lint:
golangci-lint run
23 changes: 22 additions & 1 deletion README.md
@@ -1 +1,22 @@
# las
# las

Retrieves a list of all email addresses that are on the suppression list for Amazon SES.

## Usage

```
Usage: las
Flags:
-h, --help Show help.
-r, --region=STRING The region to use ($AWS_REGION).
--version
```

```
$ las
{"EmailAddress":"foo@example.co","LastUpdateTime":"2020-12-23T01:23:45.111Z","Reason":"BOUNCE"}
{"EmailAddress":"bar@example.co","LastUpdateTime":"2020-12-23T01:23:46.22Z","Reason":"BOUNCE"}
{"EmailAddress":"zoo@example.co","LastUpdateTime":"2020-12-23T01:23:47.3Z","Reason":"BOUNCE"}
...
```
49 changes: 49 additions & 0 deletions cmd/las/main.go
@@ -0,0 +1,49 @@
package main

import (
"encoding/json"
"fmt"
"os"

"github.com/alecthomas/kong"
"github.com/aws/aws-sdk-go-v2/service/sesv2/types"
"github.com/winebarrel/las"
)

var version string

func parseArgs() *las.Options {
var CLI struct {
las.Options
Version kong.VersionFlag
}

parser := kong.Must(&CLI, kong.Vars{"version": version})
parser.Model.HelpFlag.Help = "Show help."
_, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)

return &CLI.Options
}

func main() {
opts := parseArgs()
c, err := las.NewClient(opts)

if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

err = c.ListAddSuppressedDestinations(func(sds []types.SuppressedDestinationSummary) {
for _, sd := range sds {
j, _ := json.Marshal(sd)
fmt.Println(string(j))
}
})

if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
24 changes: 24 additions & 0 deletions go.mod
@@ -0,0 +1,24 @@
module github.com/winebarrel/las

go 1.21.5

require (
github.com/alecthomas/kong v0.8.1
github.com/aws/aws-sdk-go-v2 v1.24.1
github.com/aws/aws-sdk-go-v2/config v1.26.3
github.com/aws/aws-sdk-go-v2/service/sesv2 v1.24.6
)

require (
github.com/aws/aws-sdk-go-v2/credentials v1.16.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
)
38 changes: 38 additions & 0 deletions go.sum
@@ -0,0 +1,38 @@
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA=
github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY=
github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
github.com/aws/aws-sdk-go-v2/config v1.26.3 h1:dKuc2jdp10y13dEEvPqWxqLoc0vF3Z9FC45MvuQSxOA=
github.com/aws/aws-sdk-go-v2/config v1.26.3/go.mod h1:Bxgi+DeeswYofcYO0XyGClwlrq3DZEXli0kLf4hkGA0=
github.com/aws/aws-sdk-go-v2/credentials v1.16.14 h1:mMDTwwYO9A0/JbOCOG7EOZHtYM+o7OfGWfu0toa23VE=
github.com/aws/aws-sdk-go-v2/credentials v1.16.14/go.mod h1:cniAUh3ErQPHtCQGPT5ouvSAQ0od8caTO9OOuufZOAE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
github.com/aws/aws-sdk-go-v2/service/sesv2 v1.24.6 h1:DnhxgnJsBy2IW6ZzYBIlwZ80xlDukL4cGIrXME0dpho=
github.com/aws/aws-sdk-go-v2/service/sesv2 v1.24.6/go.mod h1:n5JZkADJjQ7ro81oM6twO/ynUV8ohpxhcYmvVNUkFOQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 h1:dGrs+Q/WzhsiUKh82SfTVN66QzyulXuMDTV/G8ZxOac=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 h1:Yf2MIo9x+0tyv76GljxzqA3WtC5mw7NmazD2chwjxE4=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
69 changes: 69 additions & 0 deletions las.go
@@ -0,0 +1,69 @@
package las

import (
"context"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/retry"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/sesv2"
"github.com/aws/aws-sdk-go-v2/service/sesv2/types"
)

type Options struct {
Region string `short:"r" env:"AWS_REGION" help:"The region to use."`
}

type Client struct {
ses *sesv2.Client
}

func NewClient(opts *Options) (*Client, error) {
optFns := []func(*config.LoadOptions) error{
config.WithRetryer(func() aws.Retryer {
return retry.AddWithMaxAttempts(retry.NewStandard(), 10)
}),
}

if opts.Region != "" {
optFns = append(optFns, config.WithRegion(opts.Region))
}

cfg, err := config.LoadDefaultConfig(context.Background(), optFns...)

if err != nil {
return nil, err
}

ses := sesv2.NewFromConfig(cfg)

c := &Client{
ses: ses,
}

return c, nil
}

func (c *Client) ListAddSuppressedDestinations(f func([]types.SuppressedDestinationSummary)) error {
input := &sesv2.ListSuppressedDestinationsInput{PageSize: aws.Int32(1000)}

for {
output, err := c.ses.ListSuppressedDestinations(context.Background(), input)

if err != nil {
return err
}

f(output.SuppressedDestinationSummaries)

if output.NextToken == nil {
break
}

input.NextToken = output.NextToken
time.Sleep(1 * time.Second)
}

return nil
}

0 comments on commit af94bbd

Please sign in to comment.