Skip to content

Commit

Permalink
Ear7h issue 403 "Recursive environment variable support" (go-spatial#411
Browse files Browse the repository at this point in the history
)

* new Dicter interface for map abstractions and environment variable implementation of Dicter
* using dict internal package in config
* refactor cache, provider, atlas, cmd packages for dict
* added tests for internal packages: env, dict
* support for slices in ENV vars
* optimize slice parsing with buffered appends, added slice test cases
  • Loading branch information
ARolek authored Jun 5, 2018
1 parent 6389684 commit 401bf7b
Show file tree
Hide file tree
Showing 46 changed files with 1,984 additions and 1,275 deletions.
3 changes: 2 additions & 1 deletion atlas/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/go-spatial/tegola"
"github.com/go-spatial/tegola/basic"
"github.com/go-spatial/tegola/internal/convert"
"github.com/go-spatial/tegola/internal/dict"
"github.com/go-spatial/tegola/mvt"
"github.com/go-spatial/tegola/provider"
"github.com/go-spatial/tegola/provider/debug"
Expand Down Expand Up @@ -60,7 +61,7 @@ func (m Map) AddDebugLayers() Map {
m.Layers = layers

// setup a debug provider
debugProvider, _ := debug.NewTileProvider(map[string]interface{}{})
debugProvider, _ := debug.NewTileProvider(dict.Dict{})

m.Layers = append(layers, []Layer{
{
Expand Down
5 changes: 3 additions & 2 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/go-spatial/tegola"
"github.com/go-spatial/tegola/internal/dict"
"github.com/go-spatial/tegola/maths"
)

Expand Down Expand Up @@ -125,7 +126,7 @@ func (k Key) String() string {
// InitFunc initilize a cache given a config map.
// The InitFunc should validate the config map, and report any errors.
// This is called by the For function.
type InitFunc func(map[string]interface{}) (Interface, error)
type InitFunc func(dict.Dicter) (Interface, error)

var cache map[string]InitFunc

Expand Down Expand Up @@ -154,7 +155,7 @@ func Registered() (c []string) {
}

// For function returns a configed cache of the given type, provided the correct config map.
func For(cacheType string, config map[string]interface{}) (Interface, error) {
func For(cacheType string, config dict.Dicter) (Interface, error) {
if cache == nil {
return nil, fmt.Errorf("No cache backends registered.")
}
Expand Down
11 changes: 4 additions & 7 deletions cache/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/go-spatial/tegola"
"github.com/go-spatial/tegola/cache"
"github.com/go-spatial/tegola/util/dict"
"github.com/go-spatial/tegola/internal/dict"
)

var (
Expand All @@ -31,24 +31,21 @@ func init() {
// basepath (string): a path to where the cache will be written
// max_zoom (int): max zoom to use the cache. beyond this zoom cache Set() calls will be ignored
//
func New(config map[string]interface{}) (cache.Interface, error) {
func New(config dict.Dicter) (cache.Interface, error) {
var err error

// new filecache
fc := Cache{}

// parse the config
c := dict.M(config)

defaultMaxZoom := uint(tegola.MaxZ)
maxZoom, err := c.Uint(ConfigKeyMaxZoom, &defaultMaxZoom)
maxZoom, err := config.Uint(ConfigKeyMaxZoom, &defaultMaxZoom)
if err != nil {
return nil, err
}

fc.MaxZoom = maxZoom

fc.Basepath, err = c.String(ConfigKeyBasepath, nil)
fc.Basepath, err = config.String(ConfigKeyBasepath, nil)
if err != nil {
return nil, ErrMissingBasepath
}
Expand Down
13 changes: 7 additions & 6 deletions cache/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
"github.com/go-spatial/tegola"
"github.com/go-spatial/tegola/cache"
"github.com/go-spatial/tegola/cache/file"
"github.com/go-spatial/tegola/internal/dict"
)

func TestNew(t *testing.T) {
type tcase struct {
config map[string]interface{}
config dict.Dict
expected *file.Cache
err error
}
Expand All @@ -27,7 +28,7 @@ func TestNew(t *testing.T) {
// correct error returned
return
}
t.Errorf("%v", err)
t.Errorf("unexpected error %v", err)
return
}

Expand Down Expand Up @@ -70,7 +71,7 @@ func TestNew(t *testing.T) {
"max_zoom": "foo",
},
expected: nil,
err: fmt.Errorf("max_zoom value needs to be of type uint. Value is of type string"),
err: fmt.Errorf(`config: value mapped to "max_zoom" is string not uint`),
},
}

Expand All @@ -84,7 +85,7 @@ func TestNew(t *testing.T) {

func TestSetGetPurge(t *testing.T) {
type tcase struct {
config map[string]interface{}
config dict.Dict
key cache.Key
expected []byte
}
Expand Down Expand Up @@ -150,7 +151,7 @@ func TestSetGetPurge(t *testing.T) {

func TestSetOverwrite(t *testing.T) {
type tcase struct {
config map[string]interface{}
config dict.Dict
key cache.Key
bytes1 []byte
bytes2 []byte
Expand Down Expand Up @@ -227,7 +228,7 @@ func TestSetOverwrite(t *testing.T) {

func TestMaxZoom(t *testing.T) {
type tcase struct {
config map[string]interface{}
config dict.Dict
key cache.Key
bytes []byte
expectedHit bool
Expand Down
6 changes: 3 additions & 3 deletions cache/redis/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/go-spatial/tegola"
"github.com/go-spatial/tegola/cache"
"github.com/go-spatial/tegola/util/dict"
"github.com/go-spatial/tegola/internal/dict"
)

const CacheType = "redis"
Expand All @@ -25,7 +25,7 @@ func init() {
cache.Register(CacheType, New)
}

func New(config map[string]interface{}) (rcache cache.Interface, err error) {
func New(config dict.Dicter) (rcache cache.Interface, err error) {

// default values
defaultNetwork := "tcp"
Expand All @@ -34,7 +34,7 @@ func New(config map[string]interface{}) (rcache cache.Interface, err error) {
defaultDB := 0
defaultMaxZoom := uint(tegola.MaxZ)

c := dict.M(config)
c := config

network, err := c.String(ConfigKeyNetwork, &defaultNetwork)
if err != nil {
Expand Down
102 changes: 78 additions & 24 deletions cache/redis/redis_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package redis_test

import (
"net"
"os"
"reflect"
"strings"
"syscall"
"testing"

"github.com/go-spatial/tegola/cache"
"github.com/go-spatial/tegola/cache/redis"
"github.com/go-spatial/tegola/internal/dict"
"github.com/go-spatial/tegola/internal/ttools"
)

Expand All @@ -18,12 +21,52 @@ const TESTENV = "RUN_REDIS_TESTS"
func TestNew(t *testing.T) {
ttools.ShouldSkip(t, TESTENV)

type tc struct {
config map[string]interface{}
errMatch string
type tcase struct {
config dict.Dict
expectedErr error
}

testcases := map[string]tc{
fn := func(t *testing.T, tc tcase) {
t.Parallel()

_, err := redis.New(tc.config)
if tc.expectedErr != nil {
if err == nil {
t.Errorf("expected err %v, got nil", tc.expectedErr.Error())
return
}

// check error types
if reflect.TypeOf(err) != reflect.TypeOf(tc.expectedErr) {
t.Errorf("invalid error type. expected %T, got %T", tc.expectedErr, err)
return
}

switch e := err.(type) {
case *net.OpError:
expectedErr := tc.expectedErr.(*net.OpError)

if reflect.TypeOf(e.Err) != reflect.TypeOf(expectedErr.Err) {
t.Errorf("invalid error type. expected %T, got %T", expectedErr.Err, e.Err)
return
}
default:
// check error messages
if err.Error() != tc.expectedErr.Error() {
t.Errorf("invalid error. expected %v, got %v", tc.expectedErr, err.Error())
return
}
}

return
}
if err != nil {
t.Errorf("unexpected err: %v", err)
return
}
}

tests := map[string]tcase{
"explicit config": {
config: map[string]interface{}{
"network": "tcp",
Expand All @@ -32,50 +75,61 @@ func TestNew(t *testing.T) {
"db": 0,
"max_zoom": uint(10),
},
errMatch: "",
},
"implicit config": {
config: map[string]interface{}{},
errMatch: "",
config: map[string]interface{}{},
},
"bad address": {
config: map[string]interface{}{
"address": "127.0.0.1:6000",
},
errMatch: "connection refused",
expectedErr: &net.OpError{
Op: "dial",
Net: "tcp",
Addr: &net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 6000,
},
Err: &os.SyscallError{
Err: syscall.ECONNREFUSED,
},
},
},
"bad max_zoom": {
config: map[string]interface{}{
"max_zoom": "2",
},
errMatch: "max_zoom value needs to be of type uint. Value is of type string",
expectedErr: dict.ErrKeyType{
Key: "max_zoom",
Value: "2",
T: reflect.TypeOf(uint(0)),
},
},
"bad max_zoom 2": {
config: map[string]interface{}{
"max_zoom": -2,
},
errMatch: "max_zoom value needs to be of type uint. Value is of type int",
expectedErr: dict.ErrKeyType{
Key: "max_zoom",
Value: -2,
T: reflect.TypeOf(uint(0)),
},
},
}

for i, tc := range testcases {
_, err := redis.New(tc.config)
if err != nil {
if tc.errMatch != "" && strings.Contains(err.Error(), tc.errMatch) {
// correct error returned
continue
}
t.Errorf("[%v] unexpected err, expected to find %q in %q", i, tc.errMatch, err)
continue
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
fn(t, tc)
})
}
}

func TestSetGetPurge(t *testing.T) {
ttools.ShouldSkip(t, TESTENV)

type tc struct {
config map[string]interface{}
config dict.Dict
key cache.Key
expectedData []byte
expectedHit bool
Expand Down Expand Up @@ -150,7 +204,7 @@ func TestSetGetPurge(t *testing.T) {
func TestSetOverwrite(t *testing.T) {
ttools.ShouldSkip(t, TESTENV)
type tc struct {
config map[string]interface{}
config dict.Dict
key cache.Key
bytes1 []byte
bytes2 []byte
Expand Down Expand Up @@ -217,7 +271,7 @@ func TestSetOverwrite(t *testing.T) {
func TestMaxZoom(t *testing.T) {
ttools.ShouldSkip(t, TESTENV)
type tcase struct {
config map[string]interface{}
config dict.Dict
key cache.Key
bytes []byte
expectedHit bool
Expand Down
Loading

0 comments on commit 401bf7b

Please sign in to comment.