Skip to content

Commit 96caccc

Browse files
committed
Add 'Backends' init and 'XmlDecoder' register
1 parent 2de76f4 commit 96caccc

File tree

18 files changed

+386
-24
lines changed

18 files changed

+386
-24
lines changed

config/config.go

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package config
22

33
import (
4+
"errors"
45
"fmt"
56
"melody/encoding"
67
"regexp"
8+
"strings"
79
"time"
810
)
911

@@ -16,9 +18,10 @@ const (
1618
)
1719

1820
var (
19-
RoutingPattern = ColonRouterPatternBuilder
20-
debugPattern = "^[^/]|/__debug(/.*)?$"
21-
simpleURLKeysPattern = regexp.MustCompile(`\{([a-zA-Z\-_0-9]+)\}`)
21+
RoutingPattern = ColonRouterPatternBuilder
22+
debugPattern = "^[^/]|/__debug(/.*)?$"
23+
simpleURLKeysPattern = regexp.MustCompile(`\{([a-zA-Z\-_0-9]+)\}`)
24+
errInvalidNoOpEncoding = errors.New("can not use NoOp encoding with more than one backends connected to the same endpoint")
2225
)
2326

2427
//ServiceConfig contains all config in melody server.
@@ -29,8 +32,8 @@ type ServiceConfig struct {
2932
Host []string `mapstructure:"host"`
3033
Endpoints []*EndpointConfig `mapstructure:"endpoints"`
3134

32-
OutputEncoding string `mapstructure:"output_encoding"`
33-
CacheTTL time.Duration `mapstructure:"cache_ttl"`
35+
OutputEncoding string `mapstructure:"output_encoding"`
36+
CacheTTL time.Duration `mapstructure:"cache_ttl"`
3437

3538
MaxIdleConnsPerHost int `mapstructure:"max_idle_connections_per_host"`
3639

@@ -64,6 +67,42 @@ type EndpointConfig struct {
6467
}
6568

6669
type Backend struct {
70+
// the name of the group the response should be moved to. If empty, the response is
71+
// not changed
72+
Group string `mapstructure:"group"`
73+
// HTTP method of the request to send to the backend
74+
Method string `mapstructure:"method"`
75+
// Set of hosts of the API
76+
Host []string `mapstructure:"host"`
77+
// False if the hostname should be sanitized
78+
HostSanitizationDisabled bool `mapstructure:"disable_host_sanitize"`
79+
// URL pattern to use to locate the resource to be consumed
80+
URLPattern string `mapstructure:"url_pattern"`
81+
// set of response fields to remove. If empty, the filter id not used
82+
Blacklist []string `mapstructure:"blacklist"`
83+
// set of response fields to allow. If empty, the filter id not used
84+
Whitelist []string `mapstructure:"whitelist"`
85+
// map of response fields to be renamed and their new names
86+
Mapping map[string]string `mapstructure:"mapping"`
87+
// the encoding format
88+
Encoding string `mapstructure:"encoding"`
89+
// the response to process is a collection
90+
IsCollection bool `mapstructure:"is_collection"`
91+
// name of the field to extract to the root. If empty, the formater will do nothing
92+
Target string `mapstructure:"target"`
93+
// name of the service discovery driver to use
94+
//SD string `mapstructure:"sd"`
95+
96+
// list of keys to be replaced in the URLPattern
97+
URLKeys []string
98+
// number of concurrent calls this endpoint must send to the API
99+
ConcurrentCalls int
100+
// timeout of this backend
101+
Timeout time.Duration
102+
// decoder to use in order to parse the received response from the API
103+
Decoder encoding.Decoder `json:"-"`
104+
// Backend Extra configuration for customized behaviours
105+
ExtraConfig ExtraConfig `mapstructure:"extra_config"`
67106
}
68107

69108
//Extra config for melody
@@ -153,11 +192,20 @@ func (s *ServiceConfig) initEndpoints() error {
153192
// 初始化一些全局默认值
154193
s.initDefaultEndpoints(i)
155194

156-
//TODO NOOP encode (目前不知道noop什么意思)
195+
//判断encode为NOOP时,backends是否大于1
196+
if e.OutputEncoding == encoding.NOOP && len(e.Backends) > 1 {
197+
return errInvalidNoOpEncoding
198+
}
157199

158200
e.ExtraConfig.sanitize()
159201

160-
//TODO 初始化 Endpoints下的Backends (*)
202+
for j, b := range e.Backends {
203+
s.initDefaultBackends(i, j)
204+
205+
//TODO 初始化、解析Backends的url以及对应的参数 (*)
206+
207+
b.ExtraConfig.sanitize()
208+
}
161209
}
162210

163211
return nil
@@ -213,6 +261,28 @@ func (s *ServiceConfig) initDefaultEndpoints(i int) {
213261
}
214262
}
215263

264+
func (s *ServiceConfig) initDefaultBackends(e, b int) {
265+
endpoint := s.Endpoints[e]
266+
backend := endpoint.Backends[b]
267+
if len(backend.Host) == 0 {
268+
backend.Host = s.Host
269+
}
270+
271+
if !backend.HostSanitizationDisabled {
272+
backend.Host = s.uriParser.CleanHosts(backend.Host)
273+
}
274+
275+
if backend.Method == "" {
276+
backend.Method = endpoint.Method
277+
}
278+
279+
backend.Timeout = endpoint.Timeout
280+
backend.ConcurrentCalls = endpoint.ConcurrentCalls
281+
282+
//根据配置的encoding, 加载对应的Decoder
283+
backend.Decoder = encoding.Get(strings.ToLower(backend.Encoding))(backend.IsCollection)
284+
}
285+
216286
func (e *EndpointConfig) validate() error {
217287
matched, err := regexp.MatchString(debugPattern, e.Endpoint)
218288
if err != nil {

core/melody/encoding.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package melody
2+
3+
import xml "melody/middleware/melody-xml"
4+
5+
func RegisterEncoders() {
6+
xml.Register()
7+
//TODO RSS register
8+
}

core/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
package core
22

33
//MelodyVersion export the project version.
4-
var MelodyVersion = "0.1.1"
4+
var MelodyVersion = "0.0.1"

docs/关键的结构体说明.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,46 @@ type EndpointConfig struct {
116116
OutputEncoding string `mapstructure:"output_encoding"`
117117
}
118118
```
119+
120+
## Backend
121+
122+
```go
123+
type Backend struct {
124+
// the name of the group the response should be moved to. If empty, the response is
125+
// not changed
126+
Group string `mapstructure:"group"`
127+
// HTTP method of the request to send to the backend
128+
Method string `mapstructure:"method"`
129+
// Set of hosts of the API
130+
Host []string `mapstructure:"host"`
131+
// False if the hostname should be sanitized
132+
HostSanitizationDisabled bool `mapstructure:"disable_host_sanitize"`
133+
// URL pattern to use to locate the resource to be consumed
134+
URLPattern string `mapstructure:"url_pattern"`
135+
// set of response fields to remove. If empty, the filter id not used
136+
Blacklist []string `mapstructure:"blacklist"`
137+
// set of response fields to allow. If empty, the filter id not used
138+
Whitelist []string `mapstructure:"whitelist"`
139+
// map of response fields to be renamed and their new names
140+
Mapping map[string]string `mapstructure:"mapping"`
141+
// the encoding format
142+
Encoding string `mapstructure:"encoding"`
143+
// the response to process is a collection
144+
IsCollection bool `mapstructure:"is_collection"`
145+
// name of the field to extract to the root. If empty, the formater will do nothing
146+
Target string `mapstructure:"target"`
147+
// name of the service discovery driver to use
148+
SD string `mapstructure:"sd"`
149+
150+
// list of keys to be replaced in the URLPattern
151+
URLKeys []string
152+
// number of concurrent calls this endpoint must send to the API
153+
ConcurrentCalls int
154+
// timeout of this backend
155+
Timeout time.Duration
156+
// decoder to use in order to parse the received response from the API
157+
Decoder encoding.Decoder `json:"-"`
158+
// Backend Extra configuration for customized behaviours
159+
ExtraConfig ExtraConfig `mapstructure:"extra_config"`
160+
}
161+
```

encoding/encoding.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package encoding
2+
3+
import "io"
4+
5+
const NOOP = "no-op"
6+
7+
// NoOpDecoder implements the Decoder interface
8+
func NoOpDecoder(_ io.Reader, _ *map[string]interface{}) error { return nil }
9+
10+
func NoOpDecoderFactory(_ bool) func(io.Reader, *map[string]interface{}) error { return NoOpDecoder }
11+
12+
// Decoder a accept a param that can be read , and a target map pointer to write
13+
type Decoder func(io.Reader, *map[string]interface{}) error
14+
15+
// DecoderFactory returns a Decoder
16+
// 1. EntityDecoder {}
17+
// 2. CollectionDecoder []
18+
type DecoderFactory func(bool) func(io.Reader, *map[string]interface{}) error
19+
20+
func Get(key string) DecoderFactory {
21+
return decoders.Get(key)
22+
}
23+
24+
func Register(key string, factory DecoderFactory) error {
25+
return decoders.Register(key, factory)
26+
}

encoding/json.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,39 @@
11
package encoding
22

3+
import (
4+
"encoding/json"
5+
"io"
6+
)
7+
38
const JSON = "json"
9+
10+
func NewJSONDecoder(isCollection bool) func(io.Reader, *map[string]interface{}) error {
11+
if isCollection {
12+
return JSONCollectionDecoder()
13+
} else {
14+
return JSONDecoder()
15+
}
16+
17+
return nil
18+
}
19+
20+
func JSONCollectionDecoder() Decoder {
21+
return func(reader io.Reader, i *map[string]interface{}) error {
22+
d := json.NewDecoder(reader)
23+
d.UseNumber()
24+
return d.Decode(i)
25+
}
26+
}
27+
28+
func JSONDecoder() Decoder {
29+
return func(reader io.Reader, i *map[string]interface{}) error {
30+
var collection []interface{}
31+
d := json.NewDecoder(reader)
32+
d.UseNumber()
33+
if err := d.Decode(&collection); err != nil {
34+
return err
35+
}
36+
*i = map[string]interface{}{"collection": collection}
37+
return nil
38+
}
39+
}

encoding/register.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package encoding
2+
3+
import (
4+
"io"
5+
"melody/register"
6+
)
7+
8+
var (
9+
decoders = initDecoderRegister()
10+
defaultDecoders = map[string]func(bool) func(io.Reader, *map[string]interface{}) error{
11+
JSON: NewJSONDecoder,
12+
STRING: NewStringDecoder,
13+
NOOP: NoOpDecoderFactory,
14+
}
15+
)
16+
17+
type DecoderRegister struct {
18+
data register.Untyped
19+
}
20+
21+
func (d *DecoderRegister) Get(s string) DecoderFactory {
22+
// if can not get decoder:key = s,
23+
// return json decoder
24+
for _, v := range []string{s, JSON} {
25+
if v, ok := d.data.Get(v); ok {
26+
if decoderFactory, ok := v.(func(bool) func(io.Reader, *map[string]interface{}) error); ok {
27+
return decoderFactory
28+
}
29+
}
30+
}
31+
32+
return NewJSONDecoder
33+
}
34+
35+
func (d *DecoderRegister) Register(name string, factory DecoderFactory) error {
36+
d.data.Register(name, factory)
37+
return nil
38+
}
39+
40+
func initDecoderRegister() *DecoderRegister {
41+
decoder := &DecoderRegister{data: register.New()}
42+
for k, v := range defaultDecoders {
43+
decoder.data.Register(k, v)
44+
}
45+
return decoder
46+
}

encoding/string.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package encoding
2+
3+
import (
4+
"io"
5+
"io/ioutil"
6+
)
7+
8+
const STRING = "string"
9+
10+
func NewStringDecoder(_ bool) func(io.Reader, *map[string]interface{}) error {
11+
return StringDecoder()
12+
}
13+
14+
func StringDecoder() Decoder {
15+
return func(reader io.Reader, i *map[string]interface{}) error {
16+
data, err := ioutil.ReadAll(reader)
17+
if err != nil {
18+
return err
19+
}
20+
*i = map[string]interface{}{"content": string(data)}
21+
return nil
22+
}
23+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module melody
33
go 1.13
44

55
require (
6+
github.com/clbanning/mxj v1.8.4
67
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
78
github.com/pelletier/go-toml v1.6.0 // indirect
89
github.com/spf13/afero v1.2.2 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
88
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
99
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
1010
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
11+
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
12+
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
1113
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
1214
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
1315
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=

main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ import (
55
)
66

77
func TestCommandRun(t *testing.T) {
8-
8+
99
}

melody.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"headers_to_pass": [
2828
"*"
2929
],
30-
"backend": [
30+
"backends": [
3131
{
3232
"url_pattern": "/page",
3333
"encoding": "json",

middleware/melody-gelf/log.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,17 @@ import (
1111
const Namespace = "melody_gelf"
1212

1313
var (
14-
tcpWriter = gelf.NewTCPWriter
15-
udpWriter = gelf.NewUDPWriter
16-
ErrorConfig = fmt.Errorf("not found extra config about melody-gelf module")
14+
tcpWriter = gelf.NewTCPWriter
15+
udpWriter = gelf.NewUDPWriter
16+
ErrorConfig = fmt.Errorf("not found extra config about melody-gelf module")
1717
ErrorMissAddress = errors.New("miss gelf address to send log")
1818
)
1919

2020
type Config struct {
21-
Addr string
21+
Addr string
2222
TCPEnable bool
2323
}
2424

25-
26-
2725
func NewWriter(cfg config.ExtraConfig) (io.Writer, error) {
2826
g, ok := GetConfig(cfg).(Config)
2927
if !ok {
@@ -68,6 +66,3 @@ func GetConfig(cfg config.ExtraConfig) interface{} {
6866
TCPEnable: enable.(bool),
6967
}
7068
}
71-
72-
73-

0 commit comments

Comments
 (0)