Skip to content

Commit

Permalink
Merge pull request #1 from fujiwara/kong
Browse files Browse the repository at this point in the history
use alecthomas/kong
  • Loading branch information
fujiwara authored May 9, 2024
2 parents e4d3c23 + e018b8c commit 1b8a886
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 43 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ jobs:
- name: Check out code into the Go module directory
uses: actions/checkout@v4

- name: Build & Test
- name: Test
run: |
go test -v .
- name: Build
run: |
go generate ./cmd/aws-sdk-client-gen .
go build -o aws-sdk-client-go ./cmd/aws-sdk-client-go/main.go
go test ./...
env:
GO111MODULE: on
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ gen:
go fmt

test:
go test -v ./...
go test -v .
126 changes: 126 additions & 0 deletions cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package sdkclient_test

import (
"bytes"
"context"
"encoding/json"
"io"
"testing"

"github.com/alecthomas/kong"
"github.com/aws/aws-sdk-go-v2/aws"
sdkclient "github.com/fujiwara/aws-sdk-client-go"
)

func init() {
sdkclient.SetClientMethod("foo#Client.List", func(_ context.Context, _ aws.Config, _ json.RawMessage) (any, error) {
return []string{"a", "b", "c"}, nil
})
sdkclient.SetClientMethod("foo#Client.Get", func(_ context.Context, _ aws.Config, _ json.RawMessage) (any, error) {
return struct{ Name string }{Name: "foo"}, nil
})
sdkclient.SetClientMethod("bar#Client.List", func(_ context.Context, _ aws.Config, _ json.RawMessage) (any, error) {
return []string{"x", "y", "z"}, nil
})
sdkclient.SetClientMethod("bar#Client.Get", func(_ context.Context, _ aws.Config, _ json.RawMessage) (any, error) {
return struct{ Name string }{Name: "bar"}, nil
})
sdkclient.SetClientMethod("baz#Client.Echo", func(_ context.Context, _ aws.Config, b json.RawMessage) (any, error) {
var v any
err := json.Unmarshal(b, &v)
return v, err
})
}

type TestCase struct {
Name string
Args []string
Expect string
IsError bool
}

var TestCases = []TestCase{
{
Name: "no args (list services)",
Args: []string{},
Expect: "bar\nbaz\nfoo\n",
},
{
Name: "list methods of foo",
Args: []string{"foo"},
Expect: "Get\nList\n",
},
{
Name: "list methods of bar",
Args: []string{"bar"},
Expect: "Get\nList\n",
},
{
Name: "list methods of baz",
Args: []string{"baz"},
Expect: "Echo\n",
},
{
Name: "call foo#Client.List",
Args: []string{"foo", "List"},
Expect: "[\n \"a\",\n \"b\",\n \"c\"\n]\n",
},
{
Name: "call foo#Client.Get",
Args: []string{"foo", "Get"},
Expect: "{\n \"Name\": \"foo\"\n}\n",
},
{
Name: "call foo#Client.List",
Args: []string{"foo", "List", "help"},
Expect: "See https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/foo#Client.List\n",
},
{
Name: "call foo#Client.List -c",
Args: []string{"foo", "List", "-c"},
Expect: `["a","b","c"]`,
},
{
Name: "call bar#Client.Get --compact",
Args: []string{"bar", "Get", "--compact"},
Expect: `{"Name":"bar"}`,
},
{
Name: "call baz#Client.Echo",
Args: []string{"baz", "Echo", `{"Example": "value"}`},
Expect: "{\n \"Example\": \"value\"\n}\n",
},
}

func TestRun(t *testing.T) {
for _, tc := range TestCases {
ctx := context.Background()
t.Run(tc.Name, func(t *testing.T) {
buf := &bytes.Buffer{}
c, err := newCLI(tc.Args, buf)
if err != nil {
t.Fatal(err)
}
if err := c.Dispatch(ctx); err != nil {
t.Fatal(err)
}
if got := buf.String(); got != tc.Expect {
t.Errorf("unexpected output: got %q, expect %q", got, tc.Expect)
}
})
}
}

func newCLI(args []string, w io.Writer) (*sdkclient.CLI, error) {
c := &sdkclient.CLI{}
c.SetWriter(w)
p, err := kong.New(c)
if err != nil {
return nil, err
}
_, err = p.Parse(args)
if err != nil {
return nil, err
}
return c, nil
}
9 changes: 9 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package sdkclient

func SetClientMethod(key string, fn ClientMethod) {
clientMethods[key] = fn
}

func ClientMethods() map[string]ClientMethod {
return clientMethods
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/fujiwara/aws-sdk-client-go
go 1.21.0

require (
github.com/alecthomas/kong v0.9.0
github.com/aws/aws-sdk-go-v2 v1.26.1
github.com/aws/aws-sdk-go-v2/config v1.27.11
github.com/goccy/go-yaml v1.11.3
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA=
github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA=
Expand Down Expand Up @@ -36,6 +42,8 @@ github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/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=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
Expand Down
93 changes: 55 additions & 38 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,101 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"sort"
"strings"

"github.com/alecthomas/kong"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
)

var clientMethods = make(map[string]func(context.Context, aws.Config, json.RawMessage) (any, error))
var clientMethods = make(map[string]ClientMethod)

type ClientMethod func(context.Context, aws.Config, json.RawMessage) (any, error)

type CLI struct {
Service string `arg:"" help:"service name" default:""`
Method string `arg:"" help:"method name" default:""`
Input string `arg:"" help:"input JSON" default:"{}"`
Compact bool `short:"c" help:"compact JSON output"`

w io.Writer
}

func Run(ctx context.Context) error {
var err error
awsCfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return err
}
switch len(os.Args) {
case 1:
return listServices()
case 2:
pkgName := os.Args[1]
return listMethods(pkgName)
case 3:
pkgName := os.Args[1]
methodName := os.Args[2]
input := json.RawMessage(`{}`)
return dispatchMethod(ctx, awsCfg, pkgName, methodName, input)
case 4:
pkgName := os.Args[1]
methodName := os.Args[2]
input := json.RawMessage(os.Args[3])
return dispatchMethod(ctx, awsCfg, pkgName, methodName, input)
default:
return fmt.Errorf("too many args")
var c CLI
c.w = os.Stdout
kong.Parse(&c)
return c.Dispatch(ctx)
}

func (c *CLI) Dispatch(ctx context.Context) error {
if c.Service == "" {
return c.ListServices(ctx)
} else if c.Method == "" {
return c.ListMethods(ctx)
} else {
return c.CallMethod(ctx)
}
}

func dispatchMethod(ctx context.Context, awsCfg aws.Config, pkgName, methodName string, in json.RawMessage) error {
key := buildKey(pkgName, methodName)
if bytes.Equal(in, []byte(`help`)) {
fmt.Printf("See https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/%s\n", key)
func (c *CLI) SetWriter(w io.Writer) {
c.w = w
}

func (c *CLI) CallMethod(ctx context.Context) error {
key := buildKey(c.Service, c.Method)
if c.Input == "help" {
fmt.Fprintf(c.w, "See https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/%s\n", key)
return nil
}

fn := clientMethods[key]
if fn == nil {
return fmt.Errorf("unknown function %s", key)
}
out, err := fn(ctx, awsCfg, in)

awsCfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return err
}
out, err := fn(ctx, awsCfg, json.RawMessage(c.Input))
if err != nil {
return err
}
b, err := json.Marshal(out)
if err != nil {
return fmt.Errorf("failed to marshal response: %w", err)
}
var buf bytes.Buffer
json.Indent(&buf, b, "", " ")
buf.WriteTo(os.Stdout)
fmt.Fprintln(os.Stdout)
if !c.Compact {
var buf bytes.Buffer
json.Indent(&buf, b, "", " ")
buf.WriteString("\n")
buf.WriteTo(c.w)
} else {
io.WriteString(c.w, string(b))
}
return nil
}

func listMethods(pkgName string) error {
func (c *CLI) ListMethods(_ context.Context) error {
methods := make([]string, 0)
for name := range clientMethods {
service, method := parseKey(name)
if service == pkgName {
if service == c.Service {
methods = append(methods, method)
}
}
sort.Strings(methods)
for _, name := range methods {
fmt.Println(name)
fmt.Fprintln(c.w, name)
}
return nil
}

func listServices() error {
func (c *CLI) ListServices(_ context.Context) error {
services := make(map[string]struct{})
for name := range clientMethods {
service, _ := parseKey(name)
Expand All @@ -94,7 +111,7 @@ func listServices() error {
}
sort.Strings(names)
for _, name := range names {
fmt.Println(name)
fmt.Fprintln(c.w, name)
}
return nil
}
Expand Down

0 comments on commit 1b8a886

Please sign in to comment.