Skip to content

Commit

Permalink
Return response type based on incoming header
Browse files Browse the repository at this point in the history
  • Loading branch information
dhumphreys01 committed Mar 1, 2024
1 parent 2e468e9 commit 1babc4e
Show file tree
Hide file tree
Showing 17 changed files with 288 additions and 131 deletions.
12 changes: 0 additions & 12 deletions app/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,6 @@ type ResponseMetadata struct {
RequestId string `xml:"RequestId"`
}

/*** Error Responses ***/
type ErrorResult struct {
Type string `xml:"Type,omitempty"`
Code string `xml:"Code,omitempty"`
Message string `xml:"Message,omitempty"`
}

type ErrorResponse struct {
Result ErrorResult `xml:"Error"`
RequestId string `xml:"RequestId"`
}

type RandomLatency struct {
Min int
Max int
Expand Down
4 changes: 4 additions & 0 deletions app/fixtures/fixtures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package fixtures

var XMLNS = "http://queue.amazonaws.com/doc/2012-11-05/"
var REQUEST_ID = "request-id"
12 changes: 7 additions & 5 deletions app/fixtures/sqs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ var CreateQueueAttributes = models.Attributes{
RedriveAllowPolicy: map[string]interface{}{"this-is": "the-redrive-allow-policy"},
}

var CreateQueueResponse = app.CreateQueueResponse{
Xmlns: "http://queue.amazonaws.com/doc/2012-11-05/",
Result: app.CreateQueueResult{
QueueUrl: fmt.Sprintf("http://us-east-1.localhost:4200/100010001000/%s", QueueName),
},
var CreateQueueResult = models.CreateQueueResult{
QueueUrl: fmt.Sprintf("http://us-east-1.localhost:4200/100010001000/%s", QueueName),
}

var CreateQueueResponse = models.CreateQueueResponse{
Xmlns: "http://queue.amazonaws.com/doc/2012-11-05/",
Result: CreateQueueResult,
Metadata: app.ResponseMetadata{
RequestId: "00000000-0000-0000-0000-000000000000",
},
Expand Down
6 changes: 4 additions & 2 deletions app/gosns/gosns.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strings"
"time"

"github.com/Admiral-Piett/goaws/app/models"

"bytes"
"crypto"
"crypto/rand"
Expand Down Expand Up @@ -769,8 +771,8 @@ func extractMessageFromJSON(msg string, protocol string) (string, error) {

func createErrorResponse(w http.ResponseWriter, req *http.Request, err string) {
er := app.SnsErrors[err]
respStruct := app.ErrorResponse{
Result: app.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
respStruct := models.ErrorResponse{
Result: models.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
RequestId: "00000000-0000-0000-0000-000000000000",
}

Expand Down
20 changes: 13 additions & 7 deletions app/gosqs/gosqs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"time"

"github.com/Admiral-Piett/goaws/app/interfaces"

"github.com/Admiral-Piett/goaws/app/models"

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

func CreateQueueV1(req *http.Request) (int, interface{}) {
func CreateQueueV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
requestBody := models.NewCreateQueueRequest()
ok := utils.REQUEST_TRANSFORMER(requestBody, req)
if !ok {
Expand Down Expand Up @@ -153,7 +155,11 @@ func CreateQueueV1(req *http.Request) (int, interface{}) {
app.SyncQueues.Unlock()
}

respStruct := app.CreateQueueResponse{"http://queue.amazonaws.com/doc/2012-11-05/", app.CreateQueueResult{QueueUrl: queueUrl}, app.ResponseMetadata{RequestId: "00000000-0000-0000-0000-000000000000"}}
respStruct := models.CreateQueueResponse{
Xmlns: "http://queue.amazonaws.com/doc/2012-11-05/",
Result: models.CreateQueueResult{QueueUrl: queueUrl},
Metadata: app.ResponseMetadata{RequestId: "00000000-0000-0000-0000-000000000000"},
}
return http.StatusOK, respStruct
}

Expand Down Expand Up @@ -996,8 +1002,8 @@ func getQueueFromPath(formVal string, theUrl string) string {

func createErrorResponse(w http.ResponseWriter, req *http.Request, err string) {
er := app.SqsErrors[err]
respStruct := app.ErrorResponse{
Result: app.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
respStruct := models.ErrorResponse{
Result: models.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
RequestId: "00000000-0000-0000-0000-000000000000",
}

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

func createErrorResponseV1(err string) (int, app.ErrorResponse) {
func createErrorResponseV1(err string) (int, interfaces.AbstractResponseBody) {
er := app.SqsErrors[err]
respStruct := app.ErrorResponse{
Result: app.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
respStruct := models.ErrorResponse{
Result: models.ErrorResult{Type: er.Type, Code: er.Code, Message: er.Message},
RequestId: "00000000-0000-0000-0000-000000000000", // TODO - fix
}
return er.HttpError, respStruct
Expand Down
4 changes: 2 additions & 2 deletions app/gosqs/gosqs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2234,8 +2234,8 @@ func TestGetQueueAttributes_GetSelectedAttributes(t *testing.T) {
}

func TestCreateErrorResponseV1(t *testing.T) {
expectedResponse := app.ErrorResponse{
Result: app.ErrorResult{
expectedResponse := models.ErrorResponse{
Result: models.ErrorResult{
Type: "Not Found",
Code: "AWS.SimpleQueueService.NonExistentQueue",
Message: "The specified queue does not exist for this wsdl version.",
Expand Down
5 changes: 5 additions & 0 deletions app/interfaces/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ import (
type AbstractRequestBody interface {
SetAttributesFromForm(values url.Values)
}

type AbstractResponseBody interface {
GetResult() interface{}
GetRequestId() string
}
27 changes: 26 additions & 1 deletion app/mocks/mocks.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package mocks

import "net/url"
import (
"net/url"

af "github.com/Admiral-Piett/goaws/app/fixtures"
)

type MockRequestBody struct {
RequestFieldStr string `json:"field" schema:"field"`
Expand All @@ -19,3 +23,24 @@ func (m *MockRequestBody) SetAttributesFromForm(values url.Values) {
m.MockSetAttributesFromFormCalledWith(values)
}
}

type BaseResponse struct {
Message string `json:"Message" xml:"Message"`

MockGetResult func() interface{} `json:"-" xml:"-"`
MockGetRequestId func() string `json:"-" xml:"-"`
}

func (r BaseResponse) GetResult() interface{} {
if r.MockGetResult != nil {
return r.MockGetResult()
}
return r
}

func (r BaseResponse) GetRequestId() string {
if r.MockGetRequestId != nil {
return r.GetRequestId()
}
return af.REQUEST_ID
}
20 changes: 10 additions & 10 deletions app/models/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ func TestCreateQueueRequest_SetAttributesFromForm_success(t *testing.T) {
}
cqr.SetAttributesFromForm(form)

assert.Equal(t, 1, cqr.Attributes.DelaySeconds)
assert.Equal(t, 2, cqr.Attributes.MaximumMessageSize)
assert.Equal(t, 3, cqr.Attributes.MessageRetentionPeriod)
assert.Equal(t, StringToInt(1), cqr.Attributes.DelaySeconds)
assert.Equal(t, StringToInt(2), cqr.Attributes.MaximumMessageSize)
assert.Equal(t, StringToInt(3), cqr.Attributes.MessageRetentionPeriod)
assert.Equal(t, map[string]interface{}{"i-am": "the-policy"}, cqr.Attributes.Policy)
assert.Equal(t, 4, cqr.Attributes.ReceiveMessageWaitTimeSeconds)
assert.Equal(t, 5, cqr.Attributes.VisibilityTimeout)
assert.Equal(t, StringToInt(4), cqr.Attributes.ReceiveMessageWaitTimeSeconds)
assert.Equal(t, StringToInt(5), cqr.Attributes.VisibilityTimeout)
assert.Equal(t, expectedRedrivePolicy, cqr.Attributes.RedrivePolicy)
assert.Equal(t, map[string]interface{}{"i-am": "the-redrive-allow-policy"}, cqr.Attributes.RedriveAllowPolicy)
}
Expand Down Expand Up @@ -166,12 +166,12 @@ func TestCreateQueueRequest_SetAttributesFromForm_success_skips_invalid_values(t
}
cqr.SetAttributesFromForm(form)

assert.Equal(t, 1, cqr.Attributes.DelaySeconds)
assert.Equal(t, 262144, cqr.Attributes.MaximumMessageSize)
assert.Equal(t, 345600, cqr.Attributes.MessageRetentionPeriod)
assert.Equal(t, StringToInt(1), cqr.Attributes.DelaySeconds)
assert.Equal(t, StringToInt(262144), cqr.Attributes.MaximumMessageSize)
assert.Equal(t, StringToInt(345600), cqr.Attributes.MessageRetentionPeriod)
assert.Equal(t, map[string]interface{}(nil), cqr.Attributes.Policy)
assert.Equal(t, 10, cqr.Attributes.ReceiveMessageWaitTimeSeconds)
assert.Equal(t, 30, cqr.Attributes.VisibilityTimeout)
assert.Equal(t, StringToInt(10), cqr.Attributes.ReceiveMessageWaitTimeSeconds)
assert.Equal(t, StringToInt(30), cqr.Attributes.VisibilityTimeout)
assert.Equal(t, RedrivePolicy{}, cqr.Attributes.RedrivePolicy)
assert.Equal(t, map[string]interface{}(nil), cqr.Attributes.RedriveAllowPolicy)
}
45 changes: 45 additions & 0 deletions app/models/responses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package models

import "github.com/Admiral-Piett/goaws/app"

// NOTE: Every response in here MUST implement the `AbstractResponseBody` interface in order to be used
// in `encodeResponse`

/*** Error Responses ***/
type ErrorResult struct {
Type string `xml:"Type,omitempty"`
Code string `xml:"Code,omitempty"`
Message string `xml:"Message,omitempty"`
}

type ErrorResponse struct {
Result ErrorResult `xml:"Error"`
RequestId string `xml:"RequestId"`
}

func (r ErrorResponse) GetResult() interface{} {
return r.Result
}

func (r ErrorResponse) GetRequestId() string {
return r.RequestId
}

/*** Create Queue Response */
type CreateQueueResult struct {
QueueUrl string `json:"QueueUrl" xml:"QueueUrl"`
}

type CreateQueueResponse struct {
Xmlns string `xml:"xmlns,attr"`
Result CreateQueueResult `xml:"CreateQueueResult"`
Metadata app.ResponseMetadata `xml:"ResponseMetadata"`
}

func (r CreateQueueResponse) GetResult() interface{} {
return r.Result
}

func (r CreateQueueResponse) GetRequestId() string {
return r.Metadata.RequestId
}
38 changes: 28 additions & 10 deletions app/router/router.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package router

import (
"encoding/json"
"encoding/xml"
"io"
"net/http"
"strings"

"github.com/Admiral-Piett/goaws/app/interfaces"

log "github.com/sirupsen/logrus"

"fmt"
Expand All @@ -29,19 +32,34 @@ func New() http.Handler {
return r
}

func encodeResponse(w http.ResponseWriter, statusCode int, body interface{}) {
w.Header().Set("Content-Type", "application/xml")
w.WriteHeader(statusCode)
// TODO - replace with gorilla/schema
enc := xml.NewEncoder(w)
enc.Indent(" ", " ")
if err := enc.Encode(body); err != nil {
log.Errorf("error: %v\n", err)
func encodeResponse(w http.ResponseWriter, req *http.Request, statusCode int, body interfaces.AbstractResponseBody) {
protocol := resolveProtocol(req)
switch protocol {
case AwsJsonProtocol:
w.Header().Set("x-amzn-RequestId", body.GetRequestId())
w.Header().Set("Content-Type", "application/x-amz-json-1.0")
// Stupidly these `WriteHeader` calls have to be here, if they're at the start
// they lock the headers, at the end they're ignored.
w.WriteHeader(statusCode)
err := json.NewEncoder(w).Encode(body.GetResult())
if err != nil {
log.Errorf("Response Encoding Error: %v\nResponse: %+v", err, body)
http.Error(w, "General Error", http.StatusInternalServerError)
}
case AwsQueryProtocol:
w.Header().Set("Content-Type", "application/xml")
w.WriteHeader(statusCode)
result, err := xml.Marshal(body)
if err != nil {
log.Errorf("Response Encoding Error: %v\nResponse: %+v", err, body)
http.Error(w, "General Error", http.StatusInternalServerError)
}
_, _ = w.Write(result)
}
}

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

Expand Down Expand Up @@ -93,7 +111,7 @@ func actionHandler(w http.ResponseWriter, req *http.Request) {
jsonFn, ok := routingTableV1[action]
if ok {
statusCode, responseBody := jsonFn(req)
encodeResponse(w, statusCode, responseBody)
encodeResponse(w, req, statusCode, responseBody)
return
}
fn, ok := routingTable[action]
Expand Down
Loading

0 comments on commit 1babc4e

Please sign in to comment.