Skip to content

Commit 2eec1d4

Browse files
authoredJul 14, 2020
minor fix in routing payload modification (#1158)
1 parent 9393a66 commit 2eec1d4

File tree

2 files changed

+208
-1
lines changed

2 files changed

+208
-1
lines changed
 

‎gateway/utils/routing/modify.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func (r *Routing) modifyResponse(res *http.Response, route *config.Route, token
7070
var data []byte
7171
var err error
7272

73-
if res.Header.Get("Content-Type") == "application/json" && route.Modify.Tmpl == "" {
73+
if res.Header.Get("Content-Type") == "application/json" && route.Modify.ResTmpl != "" {
7474
data, err = ioutil.ReadAll(res.Body)
7575
if err != nil {
7676
return err

‎gateway/utils/routing/modify_test.go

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
package routing
2+
3+
import (
4+
"bytes"
5+
"io/ioutil"
6+
"net/http"
7+
"net/url"
8+
"reflect"
9+
"testing"
10+
"text/template"
11+
12+
"github.com/spaceuptech/space-cloud/gateway/config"
13+
)
14+
15+
func TestRouting_modifyResponse(t *testing.T) {
16+
type args struct {
17+
res string
18+
headers http.Header
19+
resTmpl string
20+
globalHeaders config.Headers
21+
routeHeaders config.Headers
22+
auth interface{}
23+
}
24+
tests := []struct {
25+
name string
26+
args args
27+
want string
28+
wantHeaders http.Header
29+
wantErr bool
30+
}{
31+
{
32+
name: "no modification in response even when content type header is set",
33+
args: args{
34+
res: `{"abc": "xyz"}`,
35+
headers: map[string][]string{"Content-Type": {"application/json"}},
36+
},
37+
want: `{"abc": "xyz"}`,
38+
wantHeaders: map[string][]string{"Content-Type": {"application/json"}},
39+
},
40+
{
41+
name: "set headers and no body - only global",
42+
args: args{
43+
res: `{"abc": "xyz"}`,
44+
headers: map[string][]string{},
45+
globalHeaders: config.Headers{{Key: "abc", Value: "xyz"}},
46+
},
47+
want: `{"abc": "xyz"}`,
48+
wantHeaders: map[string][]string{"Abc": {"xyz"}},
49+
},
50+
{
51+
name: "set headers and no body - only route",
52+
args: args{
53+
res: `{"abc": "xyz"}`,
54+
headers: map[string][]string{},
55+
routeHeaders: config.Headers{{Key: "abc", Value: "xyz"}},
56+
},
57+
want: `{"abc": "xyz"}`,
58+
wantHeaders: map[string][]string{"Abc": {"xyz"}},
59+
},
60+
{
61+
name: "set headers and no body - both global & route",
62+
args: args{
63+
res: `{"abc": "xyz"}`,
64+
headers: map[string][]string{},
65+
globalHeaders: config.Headers{{Key: "xyz", Value: "abc"}},
66+
routeHeaders: config.Headers{{Key: "abc", Value: "xyz"}},
67+
},
68+
want: `{"abc": "xyz"}`,
69+
wantHeaders: map[string][]string{"Abc": {"xyz"}, "Xyz": {"abc"}},
70+
},
71+
{
72+
name: "mutate body",
73+
args: args{
74+
res: `{"abc":"xyz"}`,
75+
headers: map[string][]string{"Content-Type": {"application/json"}},
76+
resTmpl: `{"res": {{marshalJSON .args}}}`,
77+
},
78+
want: `{"res":{"abc":"xyz"}}`,
79+
wantHeaders: map[string][]string{"Content-Type": {"application/json"}, "Content-Length": {"21"}},
80+
},
81+
{
82+
name: "mutate body - invalid res payload",
83+
args: args{
84+
res: `{"abc":"xyz"}as`,
85+
headers: map[string][]string{"Content-Type": {"application/json"}},
86+
resTmpl: `{"res": {{marshalJSON .args}}}`,
87+
},
88+
want: `{"abc":"xyz"}as`,
89+
wantHeaders: map[string][]string{"Content-Type": {"application/json"}},
90+
},
91+
{
92+
name: "mutate body - no json header",
93+
args: args{
94+
res: `{"abc": "xyz"}`,
95+
headers: map[string][]string{},
96+
resTmpl: `{"res": {{marshalJSON .args}}}`,
97+
},
98+
want: `{"abc": "xyz"}`,
99+
wantHeaders: map[string][]string{},
100+
},
101+
}
102+
for _, tt := range tests {
103+
t.Run(tt.name, func(t *testing.T) {
104+
// Make an instance of the routing module
105+
r := &Routing{goTemplates: make(map[string]*template.Template), globalConfig: &config.GlobalRoutesConfig{ResponseHeaders: tt.args.globalHeaders}}
106+
if tt.args.resTmpl != "" {
107+
err := r.createGoTemplate("response", "p", "id", tt.args.resTmpl)
108+
if err != nil {
109+
t.Error("Unable to parse template", err)
110+
return
111+
}
112+
}
113+
// Make an instance of the route
114+
modify := struct {
115+
Tmpl config.EndpointTemplatingEngine `json:"template,omitempty" yaml:"template,omitempty"`
116+
ReqTmpl string `json:"requestTemplate" yaml:"requestTemplate"`
117+
ResTmpl string `json:"responseTemplate" yaml:"responseTemplate"`
118+
OpFormat string `json:"outputFormat,omitempty" yaml:"outputFormat,omitempty"`
119+
RequestHeaders config.Headers `json:"headers" yaml:"headers"`
120+
ResponseHeaders config.Headers `json:"resHeaders" yaml:"resHeaders"`
121+
}{Tmpl: config.EndpointTemplatingEngineGo, ResTmpl: tt.args.resTmpl, OpFormat: "json", ResponseHeaders: tt.args.routeHeaders}
122+
route := &config.Route{ID: "id", Project: "p", Modify: modify}
123+
124+
// Make an instance of the response object
125+
res := &http.Response{Body: ioutil.NopCloser(bytes.NewBuffer([]byte(tt.args.res))), Header: tt.args.headers}
126+
127+
if err := r.modifyResponse(res, route, "", tt.args.auth); (err != nil) != tt.wantErr {
128+
t.Errorf("modifyResponse() error = %v, wantErr %v", err, tt.wantErr)
129+
return
130+
}
131+
132+
// Check if response body is correct
133+
data, _ := ioutil.ReadAll(res.Body)
134+
if got := string(data); !reflect.DeepEqual(got, tt.want) {
135+
t.Errorf("modifyResponse()[body] = %v, want %v", got, tt.want)
136+
return
137+
}
138+
139+
// Check if response headers is correct
140+
if !reflect.DeepEqual(res.Header, tt.wantHeaders) {
141+
t.Errorf("modifyResponse()[header] = %v, want %v", res.Header, tt.wantHeaders)
142+
return
143+
}
144+
})
145+
}
146+
}
147+
148+
func Test_makeQueryArguments(t *testing.T) {
149+
type args struct {
150+
url string
151+
headers map[string][]string
152+
}
153+
tests := []struct {
154+
name string
155+
args args
156+
want map[string]interface{}
157+
}{
158+
{
159+
name: "valid test case",
160+
args: args{url: "/abc/xyz?foo=bar&bool=true", headers: map[string][]string{"a1": {"b1"}, "a2": {"b2"}}},
161+
want: map[string]interface{}{
162+
"path": "/abc/xyz",
163+
"pathArray": []interface{}{"abc", "xyz"},
164+
"params": map[string]interface{}{"foo": "bar", "bool": "true"},
165+
"headers": map[string]interface{}{"A1": "b1", "A2": "b2"},
166+
},
167+
},
168+
{
169+
name: "url param without value",
170+
args: args{url: "/abc/xyz?foo=bar&bool", headers: map[string][]string{"a1": {"b1"}, "a2": {"b2"}}},
171+
want: map[string]interface{}{
172+
"path": "/abc/xyz",
173+
"pathArray": []interface{}{"abc", "xyz"},
174+
"params": map[string]interface{}{"foo": "bar", "bool": ""},
175+
"headers": map[string]interface{}{"A1": "b1", "A2": "b2"},
176+
},
177+
},
178+
{
179+
name: "multiple headers only the first one becomes available",
180+
args: args{url: "/abc/xyz?foo=bar&bool=true", headers: map[string][]string{"a1": {"b11", "b12", "b13"}, "a2": {"b2"}}},
181+
want: map[string]interface{}{
182+
"path": "/abc/xyz",
183+
"pathArray": []interface{}{"abc", "xyz"},
184+
"params": map[string]interface{}{"foo": "bar", "bool": "true"},
185+
"headers": map[string]interface{}{"A1": "b11", "A2": "b2"},
186+
},
187+
},
188+
}
189+
for _, tt := range tests {
190+
t.Run(tt.name, func(t *testing.T) {
191+
u, err := url.Parse(tt.args.url)
192+
if err != nil {
193+
t.Errorf("makeQueryArguments() - error while passing url - %v", err)
194+
return
195+
}
196+
header := http.Header{}
197+
for k, array := range tt.args.headers {
198+
for _, v := range array {
199+
header.Add(k, v)
200+
}
201+
}
202+
if got := makeQueryArguments(&http.Request{Header: header, URL: u}); !reflect.DeepEqual(got, tt.want) {
203+
t.Errorf("makeQueryArguments() = %v, want %v", got, tt.want)
204+
}
205+
})
206+
}
207+
}

0 commit comments

Comments
 (0)
Please sign in to comment.