Skip to content

Commit 3675f0f

Browse files
committed
Return response type based on incoming header
1 parent 2e468e9 commit 3675f0f

File tree

15 files changed

+245
-97
lines changed

15 files changed

+245
-97
lines changed

app/common.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,6 @@ type ResponseMetadata struct {
6262
RequestId string `xml:"RequestId"`
6363
}
6464

65-
/*** Error Responses ***/
66-
type ErrorResult struct {
67-
Type string `xml:"Type,omitempty"`
68-
Code string `xml:"Code,omitempty"`
69-
Message string `xml:"Message,omitempty"`
70-
}
71-
72-
type ErrorResponse struct {
73-
Result ErrorResult `xml:"Error"`
74-
RequestId string `xml:"RequestId"`
75-
}
76-
7765
type RandomLatency struct {
7866
Min int
7967
Max int

app/fixtures/fixtures.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package fixtures
2+
3+
var XMLNS = "http://queue.amazonaws.com/doc/2012-11-05/"
4+
var REQUEST_ID = "request-id"

app/fixtures/sqs.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ var CreateQueueAttributes = models.Attributes{
3131
RedriveAllowPolicy: map[string]interface{}{"this-is": "the-redrive-allow-policy"},
3232
}
3333

34-
var CreateQueueResponse = app.CreateQueueResponse{
35-
Xmlns: "http://queue.amazonaws.com/doc/2012-11-05/",
36-
Result: app.CreateQueueResult{
37-
QueueUrl: fmt.Sprintf("http://us-east-1.localhost:4200/100010001000/%s", QueueName),
38-
},
34+
var CreateQueueResult = models.CreateQueueResult{
35+
QueueUrl: fmt.Sprintf("http://us-east-1.localhost:4200/100010001000/%s", QueueName),
36+
}
37+
38+
var CreateQueueResponse = models.CreateQueueResponse{
39+
Xmlns: "http://queue.amazonaws.com/doc/2012-11-05/",
40+
Result: CreateQueueResult,
3941
Metadata: app.ResponseMetadata{
4042
RequestId: "00000000-0000-0000-0000-000000000000",
4143
},

app/gosns/gosns.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"strings"
1111
"time"
1212

13+
"github.com/Admiral-Piett/goaws/app/models"
14+
1315
"bytes"
1416
"crypto"
1517
"crypto/rand"
@@ -769,8 +771,8 @@ func extractMessageFromJSON(msg string, protocol string) (string, error) {
769771

770772
func createErrorResponse(w http.ResponseWriter, req *http.Request, err string) {
771773
er := app.SnsErrors[err]
772-
respStruct := app.ErrorResponse{
773-
Result: app.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
774+
respStruct := models.ErrorResponse{
775+
Result: models.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
774776
RequestId: "00000000-0000-0000-0000-000000000000",
775777
}
776778

app/gosqs/gosqs.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"strings"
1010
"time"
1111

12+
"github.com/Admiral-Piett/goaws/app/interfaces"
13+
1214
"github.com/Admiral-Piett/goaws/app/models"
1315

1416
"github.com/Admiral-Piett/goaws/app/utils"
@@ -118,7 +120,7 @@ func ListQueues(w http.ResponseWriter, req *http.Request) {
118120
}
119121
}
120122

121-
func CreateQueueV1(req *http.Request) (int, interface{}) {
123+
func CreateQueueV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
122124
requestBody := models.NewCreateQueueRequest()
123125
ok := utils.REQUEST_TRANSFORMER(requestBody, req)
124126
if !ok {
@@ -153,7 +155,11 @@ func CreateQueueV1(req *http.Request) (int, interface{}) {
153155
app.SyncQueues.Unlock()
154156
}
155157

156-
respStruct := app.CreateQueueResponse{"http://queue.amazonaws.com/doc/2012-11-05/", app.CreateQueueResult{QueueUrl: queueUrl}, app.ResponseMetadata{RequestId: "00000000-0000-0000-0000-000000000000"}}
158+
respStruct := models.CreateQueueResponse{
159+
Xmlns: "http://queue.amazonaws.com/doc/2012-11-05/",
160+
Result: models.CreateQueueResult{QueueUrl: queueUrl},
161+
Metadata: app.ResponseMetadata{RequestId: "00000000-0000-0000-0000-000000000000"},
162+
}
157163
return http.StatusOK, respStruct
158164
}
159165

@@ -996,8 +1002,8 @@ func getQueueFromPath(formVal string, theUrl string) string {
9961002

9971003
func createErrorResponse(w http.ResponseWriter, req *http.Request, err string) {
9981004
er := app.SqsErrors[err]
999-
respStruct := app.ErrorResponse{
1000-
Result: app.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
1005+
respStruct := models.ErrorResponse{
1006+
Result: models.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
10011007
RequestId: "00000000-0000-0000-0000-000000000000",
10021008
}
10031009

@@ -1009,10 +1015,10 @@ func createErrorResponse(w http.ResponseWriter, req *http.Request, err string) {
10091015
}
10101016
}
10111017

1012-
func createErrorResponseV1(err string) (int, app.ErrorResponse) {
1018+
func createErrorResponseV1(err string) (int, interfaces.AbstractResponseBody) {
10131019
er := app.SqsErrors[err]
1014-
respStruct := app.ErrorResponse{
1015-
Result: app.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
1020+
respStruct := models.ErrorResponse{
1021+
Result: models.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
10161022
RequestId: "00000000-0000-0000-0000-000000000000", // TODO - fix
10171023
}
10181024
return er.HttpError, respStruct

app/gosqs/gosqs_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,8 +2234,8 @@ func TestGetQueueAttributes_GetSelectedAttributes(t *testing.T) {
22342234
}
22352235

22362236
func TestCreateErrorResponseV1(t *testing.T) {
2237-
expectedResponse := app.ErrorResponse{
2238-
Result: app.ErrorResult{
2237+
expectedResponse := models.ErrorResponse{
2238+
Result: models.ErrorResult{
22392239
Type: "Not Found",
22402240
Code: "AWS.SimpleQueueService.NonExistentQueue",
22412241
Message: "The specified queue does not exist for this wsdl version.",

app/interfaces/interfaces.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ import (
77
type AbstractRequestBody interface {
88
SetAttributesFromForm(values url.Values)
99
}
10+
11+
type AbstractResponseBody interface {
12+
GetResult() interface{}
13+
GetRequestId() string
14+
}

app/mocks/mocks.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package mocks
22

3-
import "net/url"
3+
import (
4+
"net/url"
5+
6+
af "github.com/Admiral-Piett/goaws/app/fixtures"
7+
)
48

59
type MockRequestBody struct {
610
RequestFieldStr string `json:"field" schema:"field"`
@@ -19,3 +23,24 @@ func (m *MockRequestBody) SetAttributesFromForm(values url.Values) {
1923
m.MockSetAttributesFromFormCalledWith(values)
2024
}
2125
}
26+
27+
type BaseResponse struct {
28+
Message string `json:"Message" xml:"Message"`
29+
30+
MockGetResult func() interface{} `json:"-" xml:"-"`
31+
MockGetRequestId func() string `json:"-" xml:"-"`
32+
}
33+
34+
func (r BaseResponse) GetResult() interface{} {
35+
if r.MockGetResult != nil {
36+
return r.MockGetResult()
37+
}
38+
return r
39+
}
40+
41+
func (r BaseResponse) GetRequestId() string {
42+
if r.MockGetRequestId != nil {
43+
return r.GetRequestId()
44+
}
45+
return af.REQUEST_ID
46+
}

app/models/models_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ func TestCreateQueueRequest_SetAttributesFromForm_success(t *testing.T) {
7272
}
7373
cqr.SetAttributesFromForm(form)
7474

75-
assert.Equal(t, 1, cqr.Attributes.DelaySeconds)
76-
assert.Equal(t, 2, cqr.Attributes.MaximumMessageSize)
77-
assert.Equal(t, 3, cqr.Attributes.MessageRetentionPeriod)
75+
assert.Equal(t, StringToInt(1), cqr.Attributes.DelaySeconds)
76+
assert.Equal(t, StringToInt(2), cqr.Attributes.MaximumMessageSize)
77+
assert.Equal(t, StringToInt(3), cqr.Attributes.MessageRetentionPeriod)
7878
assert.Equal(t, map[string]interface{}{"i-am": "the-policy"}, cqr.Attributes.Policy)
79-
assert.Equal(t, 4, cqr.Attributes.ReceiveMessageWaitTimeSeconds)
80-
assert.Equal(t, 5, cqr.Attributes.VisibilityTimeout)
79+
assert.Equal(t, StringToInt(4), cqr.Attributes.ReceiveMessageWaitTimeSeconds)
80+
assert.Equal(t, StringToInt(5), cqr.Attributes.VisibilityTimeout)
8181
assert.Equal(t, expectedRedrivePolicy, cqr.Attributes.RedrivePolicy)
8282
assert.Equal(t, map[string]interface{}{"i-am": "the-redrive-allow-policy"}, cqr.Attributes.RedriveAllowPolicy)
8383
}
@@ -166,12 +166,12 @@ func TestCreateQueueRequest_SetAttributesFromForm_success_skips_invalid_values(t
166166
}
167167
cqr.SetAttributesFromForm(form)
168168

169-
assert.Equal(t, 1, cqr.Attributes.DelaySeconds)
170-
assert.Equal(t, 262144, cqr.Attributes.MaximumMessageSize)
171-
assert.Equal(t, 345600, cqr.Attributes.MessageRetentionPeriod)
169+
assert.Equal(t, StringToInt(1), cqr.Attributes.DelaySeconds)
170+
assert.Equal(t, StringToInt(262144), cqr.Attributes.MaximumMessageSize)
171+
assert.Equal(t, StringToInt(345600), cqr.Attributes.MessageRetentionPeriod)
172172
assert.Equal(t, map[string]interface{}(nil), cqr.Attributes.Policy)
173-
assert.Equal(t, 10, cqr.Attributes.ReceiveMessageWaitTimeSeconds)
174-
assert.Equal(t, 30, cqr.Attributes.VisibilityTimeout)
173+
assert.Equal(t, StringToInt(10), cqr.Attributes.ReceiveMessageWaitTimeSeconds)
174+
assert.Equal(t, StringToInt(30), cqr.Attributes.VisibilityTimeout)
175175
assert.Equal(t, RedrivePolicy{}, cqr.Attributes.RedrivePolicy)
176176
assert.Equal(t, map[string]interface{}(nil), cqr.Attributes.RedriveAllowPolicy)
177177
}

app/models/responses.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package models
2+
3+
import "github.com/Admiral-Piett/goaws/app"
4+
5+
// NOTE: Every response in here MUST implement the `AbstractResponseBody` interface in order to be used
6+
// in `encodeResponse`
7+
8+
/*** Error Responses ***/
9+
type ErrorResult struct {
10+
Type string `xml:"Type,omitempty"`
11+
Code string `xml:"Code,omitempty"`
12+
Message string `xml:"Message,omitempty"`
13+
}
14+
15+
type ErrorResponse struct {
16+
Result ErrorResult `xml:"Error"`
17+
RequestId string `xml:"RequestId"`
18+
}
19+
20+
func (r ErrorResponse) GetResult() interface{} {
21+
return r.Result
22+
}
23+
24+
func (r ErrorResponse) GetRequestId() string {
25+
return r.RequestId
26+
}
27+
28+
/*** Create Queue Response */
29+
type CreateQueueResult struct {
30+
QueueUrl string `json:"QueueUrl" xml:"QueueUrl"`
31+
}
32+
33+
type CreateQueueResponse struct {
34+
Xmlns string `xml:"xmlns,attr"`
35+
Result CreateQueueResult `xml:"CreateQueueResult"`
36+
Metadata app.ResponseMetadata `xml:"ResponseMetadata"`
37+
}
38+
39+
func (r CreateQueueResponse) GetResult() interface{} {
40+
return r.Result
41+
}
42+
43+
func (r CreateQueueResponse) GetRequestId() string {
44+
return r.Metadata.RequestId
45+
}

app/router/router.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package router
22

33
import (
4+
"encoding/json"
45
"encoding/xml"
56
"io"
67
"net/http"
78
"strings"
89

10+
"github.com/Admiral-Piett/goaws/app/interfaces"
11+
912
log "github.com/sirupsen/logrus"
1013

1114
"fmt"
@@ -29,19 +32,34 @@ func New() http.Handler {
2932
return r
3033
}
3134

32-
func encodeResponse(w http.ResponseWriter, statusCode int, body interface{}) {
33-
w.Header().Set("Content-Type", "application/xml")
34-
w.WriteHeader(statusCode)
35-
// TODO - replace with gorilla/schema
36-
enc := xml.NewEncoder(w)
37-
enc.Indent(" ", " ")
38-
if err := enc.Encode(body); err != nil {
39-
log.Errorf("error: %v\n", err)
35+
func encodeResponse(w http.ResponseWriter, req *http.Request, statusCode int, body interfaces.AbstractResponseBody) {
36+
protocol := resolveProtocol(req)
37+
switch protocol {
38+
case AwsJsonProtocol:
39+
w.Header().Set("x-amzn-RequestId", body.GetRequestId())
40+
w.Header().Set("Content-Type", "application/x-amz-json-1.0")
41+
// Stupidly these `WriteHeader` calls have to be here, if they're at the start
42+
// they lock the headers, at the end they're ignored.
43+
w.WriteHeader(statusCode)
44+
err := json.NewEncoder(w).Encode(body.GetResult())
45+
if err != nil {
46+
log.Errorf("Response Encoding Error: %v\nResponse: %+v", err, body)
47+
http.Error(w, "General Error", http.StatusInternalServerError)
48+
}
49+
case AwsQueryProtocol:
50+
w.Header().Set("Content-Type", "application/xml")
51+
w.WriteHeader(statusCode)
52+
result, err := xml.Marshal(body)
53+
if err != nil {
54+
log.Errorf("Response Encoding Error: %v\nResponse: %+v", err, body)
55+
http.Error(w, "General Error", http.StatusInternalServerError)
56+
}
57+
_, _ = w.Write(result)
4058
}
4159
}
4260

4361
// V1 - includes JSON Support (and of course the old XML).
44-
var routingTableV1 = map[string]func(r *http.Request) (int, interface{}){
62+
var routingTableV1 = map[string]func(r *http.Request) (int, interfaces.AbstractResponseBody){
4563
"CreateQueue": sqs.CreateQueueV1,
4664
}
4765

@@ -93,7 +111,7 @@ func actionHandler(w http.ResponseWriter, req *http.Request) {
93111
jsonFn, ok := routingTableV1[action]
94112
if ok {
95113
statusCode, responseBody := jsonFn(req)
96-
encodeResponse(w, statusCode, responseBody)
114+
encodeResponse(w, req, statusCode, responseBody)
97115
return
98116
}
99117
fn, ok := routingTable[action]

0 commit comments

Comments
 (0)