-
Notifications
You must be signed in to change notification settings - Fork 20
/
datastore_server.go
130 lines (111 loc) · 3.25 KB
/
datastore_server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package dsmock
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"regexp"
"strings"
)
type getManyRequest struct {
Keys []string `json:"requests"`
}
// DatastoreServer simulates the Datastore-Service. Only the methods required by the
// autoupdate-service are supported. This is currently only the getMany method.
//
// Has to be created with NewDatastoreServer.
type DatastoreServer struct {
TS *httptest.Server
RequestCount int
RequestedKeys [][]string
Values *datastoreValues
c chan map[string][]byte
}
// NewDatastoreServer creates a new fake DatastoreServer.
//
// It creates a webserver that handels get_many requests like the reald
// datastore-reader.
//
// If the given channel is closed, the server shuts down.
func NewDatastoreServer(closed <-chan struct{}, data map[string][]byte) *DatastoreServer {
d := &DatastoreServer{
Values: newDatastoreValues(data),
c: make(chan map[string][]byte),
}
d.TS = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data getManyRequest
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
http.Error(w, fmt.Sprintf("Invalid json input: %v", err), http.StatusBadRequest)
return
}
defer r.Body.Close()
d.RequestedKeys = append(d.RequestedKeys, data.Keys)
responceData := make(map[string]map[string]map[string]json.RawMessage)
for _, key := range data.Keys {
if !validKey(key) {
http.Error(w, "Key is invalid: "+key, 400)
}
value, err := d.Values.value(key)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if value == nil {
continue
}
keyParts := strings.SplitN(key, "/", 3)
if len(keyParts) != 3 {
http.Error(w, fmt.Sprintf("invalid key %s", key), 500)
return
}
if _, ok := responceData[keyParts[0]]; !ok {
responceData[keyParts[0]] = make(map[string]map[string]json.RawMessage)
}
if _, ok := responceData[keyParts[0]][keyParts[1]]; !ok {
responceData[keyParts[0]][keyParts[1]] = make(map[string]json.RawMessage)
}
responceData[keyParts[0]][keyParts[1]][keyParts[2]] = value
}
if err := json.NewEncoder(w).Encode(responceData); err != nil {
http.Error(w, fmt.Sprintf("Error encoding responceData `%s`: %v", responceData, err), 500)
return
}
d.RequestCount++
}))
go func() {
<-closed
d.TS.Close()
}()
return d
}
// Update returnes keys that have changed. Blocks until keys are send with
// the Send-method.
func (d *DatastoreServer) Update(ctx context.Context) (map[string][]byte, error) {
select {
case v := <-d.c:
d.Values.set(v)
return v, nil
case <-ctx.Done():
return nil, ctx.Err()
}
}
// Send sends keys to the mock that can be received with Update().
func (d *DatastoreServer) Send(values map[string][]byte) {
d.c <- values
}
// Requests returns all keys that where requested.
func (d *DatastoreServer) Requests() [][]string {
return d.RequestedKeys
}
// ResetRequests resets the returnvalue of Requests().
func (d *DatastoreServer) ResetRequests() {
d.RequestedKeys = make([][]string, 0)
}
func validKey(key string) bool {
match, err := regexp.MatchString(`^([a-z]+|[a-z][a-z_]*[a-z])/[1-9][0-9]*/[a-z][a-z0-9_]*\$?[a-z0-9_]*$`, key)
if err != nil {
panic(err)
}
return match
}