Skip to content

Commit 253e591

Browse files
author
ktr
authored
Impl header command (#23)
1 parent 0467b08 commit 253e591

File tree

17 files changed

+341
-36
lines changed

17 files changed

+341
-36
lines changed

adapter/controller/cli.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,14 @@ func isCommandLineMode(opt *Options) bool {
203203
}
204204

205205
func setupEnv(conf *config.Config, opt *Options) (*entity.Env, error) {
206-
207206
if len(opt.Header) > 0 {
208207
for _, h := range opt.Header {
209208
s := strings.SplitN(h, "=", 2)
210209
if len(s) != 2 {
211210
return nil, errors.New(`header must be specified "key=val" format`)
212211
}
213212
key, val := s[0], s[1]
214-
conf.Env.Request.Header = append(conf.Env.Request.Header, config.Header{Key: key, Value: val})
213+
conf.Env.Request.Header = append(conf.Env.Request.Header, config.Header{Key: key, Val: val})
215214
}
216215
}
217216
if opt.Host != "" {

adapter/controller/repl.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/ktr0731/evans/config"
1313
"github.com/ktr0731/evans/entity"
1414
"github.com/ktr0731/evans/usecase/port"
15+
shellstring "github.com/ktr0731/go-shellstring"
1516
homedir "github.com/mitchellh/go-homedir"
1617
"github.com/pkg/errors"
1718
)
@@ -37,6 +38,7 @@ func NewREPL(config *config.REPL, env *entity.Env, ui ui, inputPort port.InputPo
3738
"package": &packageCommand{inputPort},
3839
"service": &serviceCommand{inputPort},
3940
"show": &showCommand{inputPort},
41+
"header": &headerCommand{inputPort},
4042
}
4143
repl := &REPL{
4244
ui: ui,
@@ -68,7 +70,15 @@ func NewREPL(config *config.REPL, env *entity.Env, ui ui, inputPort port.InputPo
6870
}
6971

7072
func (r *REPL) eval(l string) (string, error) {
71-
part := strings.Split(l, " ")
73+
// trim quote
74+
// e.g. key='foo' is interpreted to `foo`
75+
// key='foo bar' is `foo bar`
76+
// key='"foo bar"' is `"foo bar"`
77+
// key=foo bar is also `foo bar`
78+
part, err := shellstring.Parse(l)
79+
if err != nil {
80+
return "", err
81+
}
7282

7383
if part[0] == "help" {
7484
r.showHelp(r.cmds)

adapter/controller/repl_commands.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package controller
22

33
import (
44
"bytes"
5+
"fmt"
56
"io"
67
"strings"
8+
"unicode"
79

10+
"github.com/ktr0731/evans/entity"
811
"github.com/ktr0731/evans/usecase/port"
912
"github.com/pkg/errors"
1013
)
@@ -109,7 +112,7 @@ func (c *showCommand) Synopsis() string {
109112
}
110113

111114
func (c *showCommand) Help() string {
112-
return "usage: show <package | service | message | rpc>"
115+
return "usage: show <package | service | message | rpc | header>"
113116
}
114117

115118
func (c *showCommand) Validate(args []string) error {
@@ -132,6 +135,8 @@ func (c *showCommand) Run(args []string) (string, error) {
132135
params.Type = port.ShowTypeMessage
133136
case "a", "r", "rpc", "api":
134137
params.Type = port.ShowTypeRPC
138+
case "h", "header", "headers":
139+
params.Type = port.ShowTypeHeader
135140
default:
136141
return "", errors.Wrap(ErrUnknownTarget, target)
137142
}
@@ -170,6 +175,53 @@ func (c *callCommand) Run(args []string) (string, error) {
170175
return read(res)
171176
}
172177

178+
type headerCommand struct {
179+
inputPort port.InputPort
180+
}
181+
182+
func (c *headerCommand) Synopsis() string {
183+
return "set/unset headers to each request. if header value is empty, the header is removed."
184+
}
185+
186+
func (c *headerCommand) Help() string {
187+
return "usage: header <key>=<value>[, <key>=<value>...]"
188+
}
189+
190+
func (c *headerCommand) Validate(args []string) error {
191+
if len(args) < 1 {
192+
return errors.Wrap(ErrArgumentRequired, "<key>=<value> or <key>")
193+
}
194+
return nil
195+
}
196+
197+
func (c *headerCommand) Run(args []string) (string, error) {
198+
headers := []*entity.Header{}
199+
for _, h := range args {
200+
sp := strings.SplitN(h, "=", 2)
201+
header := &entity.Header{
202+
Key: sp[0],
203+
}
204+
for _, r := range sp[0] {
205+
if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '-' && r != '_' && r != '.' {
206+
return "", fmt.Errorf("invalid char in key: %c", r)
207+
}
208+
}
209+
// remove the key
210+
if len(sp) == 1 || sp[1] == "" {
211+
header.NeedToRemove = true
212+
} else {
213+
header.Val = sp[1]
214+
}
215+
headers = append(headers, header)
216+
}
217+
params := &port.HeaderParams{headers}
218+
res, err := c.inputPort.Header(params)
219+
if err != nil {
220+
return "", err
221+
}
222+
return read(res)
223+
}
224+
173225
func read(r io.Reader) (string, error) {
174226
if r == nil {
175227
return "", nil
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package controller
2+
3+
import (
4+
"io"
5+
"testing"
6+
7+
"github.com/ktr0731/evans/entity"
8+
"github.com/ktr0731/evans/usecase/port"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
type headInputPort struct {
14+
port.InputPort
15+
received *port.HeaderParams
16+
}
17+
18+
func (i *headInputPort) Header(p *port.HeaderParams) (io.Reader, error) {
19+
i.received = p
20+
return nil, nil
21+
}
22+
23+
func Test_headCommand(t *testing.T) {
24+
cases := map[string]struct {
25+
in string
26+
expected *entity.Header
27+
hasError bool
28+
}{
29+
"normal": {in: "tanamachi=kaoru", expected: &entity.Header{Key: "tanamachi", Val: "kaoru"}},
30+
"normal (has a double-quote)": {in: `nanasaki=ai"`, expected: &entity.Header{Key: "nanasaki", Val: `ai"`}},
31+
"normal (has a = in value)": {in: "morishima=lovely=haruka", expected: &entity.Header{Key: "morishima", Val: "lovely=haruka"}},
32+
"remove key1": {in: "amagami-_.0", expected: &entity.Header{Key: "amagami-_.0", NeedToRemove: true}},
33+
"remove key2": {in: "rihoko=", expected: &entity.Header{Key: "rihoko", Val: "", NeedToRemove: true}},
34+
}
35+
36+
for name, c := range cases {
37+
t.Run(name, func(t *testing.T) {
38+
inputPort := &headInputPort{}
39+
cmd := &headerCommand{inputPort}
40+
41+
_, err := cmd.Run([]string{c.in})
42+
if c.hasError {
43+
require.Error(t, err)
44+
return
45+
}
46+
require.NoError(t, err)
47+
assert.Exactly(t, c.expected, inputPort.received.Headers[0])
48+
})
49+
}
50+
}

adapter/controller/repl_completer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func (c *completer) complete(d prompt.Document) []prompt.Suggest {
2929
{Text: "service"},
3030
{Text: "message"},
3131
{Text: "rpc"},
32+
{Text: "header"},
3233
}
3334
}
3435

adapter/presenter/stub.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ func (p *StubPresenter) Show(showable port.Showable) (io.Reader, error) {
3232
}
3333

3434
func (p *StubPresenter) Header() (io.Reader, error) {
35-
panic("not supported yet")
3635
return nil, nil
3736
}
3837

config/config.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ type Server struct {
2626
}
2727

2828
type Header struct {
29-
Key string `toml:"key"`
30-
Value string `toml:"value"`
29+
Key string `toml:"key"`
30+
Val string `toml:"value"`
3131
}
3232

3333
type Request struct {
@@ -49,7 +49,6 @@ type REPL struct {
4949

5050
type Env struct {
5151
Server *Server `toml:"-"`
52-
AncestorDelimiter string `default:":"`
5352
InputPromptFormat string `default:"{ancestor}{name} ({type}) => " toml:"inputPromptFormat"`
5453
Request *Request `toml:"request"`
5554
}
@@ -68,8 +67,8 @@ type Config struct {
6867
}
6968

7069
type Default struct {
71-
Package string `toml:"package"`
72-
Service string `toml:"service"`
70+
Package string `toml:"package" default:""`
71+
Service string `toml:"service" default:""`
7372
}
7473

7574
type Log struct {
@@ -91,17 +90,17 @@ func init() {
9190
conf.REPL.Server = conf.Server
9291
conf.Env.Server = conf.Server
9392

94-
local, err := getLocalConfig()
93+
mConfig, err = configure.NewConfigure(conf.Meta.Path, conf, nil)
9594
if err != nil {
9695
panic(err)
9796
}
9897

99-
applyLocalConfig(&conf, local)
100-
101-
mConfig, err = configure.NewConfigure(conf.Meta.Path, conf, nil)
98+
local, err := getLocalConfig()
10299
if err != nil {
103100
panic(err)
104101
}
102+
103+
applyLocalConfig(&conf, local)
105104
}
106105

107106
func Get() *Config {

entity/env.go

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package entity
22

33
import (
4+
"fmt"
45
"strings"
56

67
"github.com/golang/protobuf/protoc-gen-go/descriptor"
@@ -29,17 +30,15 @@ type Environment interface {
2930
RPC(name string) (*RPC, error)
3031

3132
Headers() []*Header
33+
AddHeader(header *Header) error
34+
RemoveHeader(key string)
3235

3336
UsePackage(name string) error
3437
UseService(name string) error
3538

3639
DSN() string
3740
}
3841

39-
type Header struct {
40-
Key, Val string
41-
}
42-
4342
// pkgList is used by showing all packages
4443
// pkg is used by extract a package by package name
4544
type cache struct {
@@ -52,20 +51,25 @@ type state struct {
5251
currentService string
5352
}
5453

55-
type Env struct {
56-
pkgs Packages
57-
58-
state state
54+
type option struct {
55+
headers []*Header
56+
}
5957

58+
type Env struct {
59+
pkgs Packages
60+
state state
61+
option option
6062
config *config.Env
61-
62-
cache cache
63+
cache cache
6364
}
6465

6566
func New(pkgs Packages, config *config.Env) (*Env, error) {
6667
return &Env{
6768
pkgs: pkgs,
6869
config: config,
70+
option: option{
71+
// headers: config.Request.Header,
72+
},
6973
cache: cache{
7074
pkg: map[string]*Package{},
7175
},
@@ -142,13 +146,45 @@ func (e *Env) Message(name string) (*Message, error) {
142146

143147
func (e *Env) Headers() (headers []*Header) {
144148
headers = make([]*Header, 0, len(e.config.Request.Header))
145-
for _, header := range e.config.Request.Header {
146-
headers = append(headers, &Header{Key: header.Key, Val: header.Value})
149+
for _, header := range e.option.headers {
150+
headers = append(headers, &Header{Key: header.Key, Val: header.Val})
147151
}
148152

149153
return headers
150154
}
151155

156+
func (e *Env) AddHeader(h *Header) error {
157+
_, header := e.findHeader(h.Key)
158+
if header != nil {
159+
return fmt.Errorf("already registered key: %s", h.Key)
160+
}
161+
e.option.headers = append(e.option.headers, h)
162+
return nil
163+
}
164+
165+
func (e *Env) RemoveHeader(key string) {
166+
i, h := e.findHeader(key)
167+
// not found
168+
if h == nil {
169+
return
170+
}
171+
if len(e.option.headers) == i+1 {
172+
e.option.headers = e.option.headers[:i]
173+
} else {
174+
e.option.headers = append(e.option.headers[:i], e.option.headers[i+1:]...)
175+
}
176+
return
177+
}
178+
179+
func (e *Env) findHeader(key string) (int, *Header) {
180+
for i, pair := range e.option.headers {
181+
if pair.Key == key {
182+
return i, pair
183+
}
184+
}
185+
return 0, nil
186+
}
187+
152188
func (e *Env) RPC(name string) (*RPC, error) {
153189
rpcs, err := e.RPCs()
154190
if err != nil {

0 commit comments

Comments
 (0)