Skip to content

Commit

Permalink
lib: Add api-gateway-response helpers (#20)
Browse files Browse the repository at this point in the history
- api gateway response helpers
- assertion functions helpers
- update mod sum
  • Loading branch information
joppetburger authored Jul 10, 2024
1 parent b40aac3 commit 7aa1b75
Show file tree
Hide file tree
Showing 22 changed files with 469 additions and 36 deletions.
35 changes: 35 additions & 0 deletions api-gateway-response/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package apigatewayresponse

import "os"

var allowHeaders = os.Getenv("CORS_ALLOWED_HEADERS")
var allowOrigins = os.Getenv("CORS_ALLOWED_ORIGINS")
var allowMethods = os.Getenv("CORS_ALLOWED_METHODS")

var PreflightHttpHeaders = map[string]string{
"Access-Control-Allow-Origin": allowOrigins,
"Access-Control-Allow-Methods": "OPTIONS",
"Access-Control-Allow-Headers": allowHeaders,
}

var HttpHeaders = map[string]string{
"Access-Control-Allow-Origin": allowOrigins,
"Access-Control-Allow-Methods": allowMethods,
"Access-Control-Allow-Headers": allowHeaders,
"Content-Type": "application/json",
}

type ErrorResponse struct {
Message string `json:"message"`
Code string `json:"code"`
}

type SuccessResponseBody struct {
Results any `json:"results"`
Metadata any `json:"metadata"`
}

type PaginationMetadata struct {
PageCount int `json:"max_page"`
ResultCount int `json:"results_per_page"`
}
41 changes: 41 additions & 0 deletions api-gateway-response/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package apigatewayresponse

import (
"encoding/json"

"github.com/aws/aws-lambda-go/events"
)

type MultipleErrorsResponseType struct {
Errors []ErrorResponse `json:"errors"`
}

func MultipleErrorsResponse(status int, errors []ErrorResponse) (events.APIGatewayProxyResponse, error) {
response := events.APIGatewayProxyResponse{
Headers: HttpHeaders,
StatusCode: status,
}

body := MultipleErrorsResponseType{
Errors: errors,
}

rData, _ := json.Marshal(body)

response.Body = string(rData)

return response, nil
}

func SingleErrorResponse(status int, err ErrorResponse) (events.APIGatewayProxyResponse, error) {
response := events.APIGatewayProxyResponse{
Headers: HttpHeaders,
StatusCode: status,
}

rData, _ := json.Marshal(err)

response.Body = string(rData)

return response, nil
}
106 changes: 106 additions & 0 deletions api-gateway-response/error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package apigatewayresponse

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"testing"

"github.com/scrambledeggs/booky-go-common/assert"
)

func TestSingleErrorResponse(t *testing.T) {
status := http.StatusBadRequest

err := errors.New("invalid arguments")

errorObj := ErrorResponse{
Message: err.Error(),
Code: "INVALID_ARGUMENTS",
}

response, err := SingleErrorResponse(status, errorObj)

var responseBody ErrorResponse

json.Unmarshal([]byte(response.Body), &responseBody)

assert.ShouldBeNil(t, err)

assert.DeepEqual(t, responseBody, errorObj, "invalid value for error response")

assert.Equal(t, response.StatusCode, status, "invalid status code")
}

func TestMultipleErrorsResponse(t *testing.T) {
status := http.StatusInternalServerError

err1 := errors.New("invalid name")

error1Obj := ErrorResponse{
Message: err1.Error(),
Code: "INVALID_ARGUMENTS",
}

err2 := errors.New("invalid slug")

error2Obj := ErrorResponse{
Message: err2.Error(),
Code: "INVALID_ARGUMENTS",
}

response, err := MultipleErrorsResponse(status, []ErrorResponse{error1Obj, error2Obj})

var responseBody MultipleErrorsResponseType

json.Unmarshal([]byte(response.Body), &responseBody)

assert.ShouldBeNil(t, err)

assert.DeepEqual(t, responseBody.Errors[0], error1Obj, "invalid error value for first element")
assert.DeepEqual(t, responseBody.Errors[1], error2Obj, "invalid error value for second element")

assert.Equal(t, response.StatusCode, status, "invalid status code")
}

func ExampleSingleErrorResponse() {
status := http.StatusBadRequest

err := errors.New("invalid arguments")

errorObj := ErrorResponse{
Message: err.Error(),
Code: "INVALID_ARGUMENTS",
}

response, _ := SingleErrorResponse(status, errorObj)

fmt.Println(response.Body, response.StatusCode)

// Output: {"message":"invalid arguments","code":"INVALID_ARGUMENTS"} 400
}

func ExampleMultipleErrorsResponse() {
status := http.StatusInternalServerError

err1 := errors.New("invalid name")

error1Obj := ErrorResponse{
Message: err1.Error(),
Code: "INVALID_ARGUMENTS",
}

err2 := errors.New("invalid slug")

error2Obj := ErrorResponse{
Message: err2.Error(),
Code: "INVALID_ARGUMENTS",
}

response, _ := MultipleErrorsResponse(status, []ErrorResponse{error1Obj, error2Obj})

fmt.Println(response.Body, response.StatusCode)

// Output: {"errors":[{"message":"invalid name","code":"INVALID_ARGUMENTS"},{"message":"invalid slug","code":"INVALID_ARGUMENTS"}]} 500
}
16 changes: 16 additions & 0 deletions api-gateway-response/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module github.com/scrambledeggs/booky-go-common/apigatewayresponse

go 1.21.1

require (
github.com/aws/aws-lambda-go v1.47.0
github.com/stretchr/testify v1.8.4
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
13 changes: 13 additions & 0 deletions api-gateway-response/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
github.com/aws/aws-lambda-go v1.47.0 h1:0H8s0vumYx/YKs4sE7YM0ktwL2eWse+kfopsRI1sXVI=
github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
45 changes: 45 additions & 0 deletions api-gateway-response/success.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package apigatewayresponse

import (
"encoding/json"
"reflect"

"github.com/aws/aws-lambda-go/events"
)

func SuccessResponse(status int, data ...any) (events.APIGatewayProxyResponse, error) {
var metadata any = nil
var strBody []byte

response := events.APIGatewayProxyResponse{
Headers: HttpHeaders,
StatusCode: status,
}

isSlice := IsSlice(data[0])

if isSlice {
body := SuccessResponseBody{
Results: data[0],
}

if len(data) > 1 {
metadata = data[1]
}

body.Metadata = metadata

strBody, _ = json.Marshal(body)

} else {
strBody, _ = json.Marshal(data[0])
}

response.Body = string(strBody)

return response, nil
}

func IsSlice(v any) bool {
return reflect.TypeOf(v).Kind() == reflect.Slice
}
107 changes: 107 additions & 0 deletions api-gateway-response/success_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package apigatewayresponse

import (
"encoding/json"
"fmt"
"net/http"
"testing"

"github.com/scrambledeggs/booky-go-common/assert"
)

func TestSuccessResponseSingleResult(t *testing.T) {
status := http.StatusOK

body := map[string]string{
"naknang": "sonof",
"patatas": "potato",
}

response, err := SuccessResponse(status, body)

var responseBody map[string]string

json.Unmarshal([]byte(response.Body), &responseBody)

assert.ShouldBeNil(t, err)

assert.DeepEqual(t, responseBody, body, "invalid value for body")

assert.Equal(t, response.StatusCode, status, "invalid status code")
}

func TestSuccessResponseMultipleResults(t *testing.T) {
status := http.StatusOK

body := []map[string]string{
{"naknang": "sonof", "patatas": "potato"},
{"sonof": "naknang", "potato": "patatas"},
}

metadata := PaginationMetadata{
PageCount: 10,
ResultCount: 100,
}

response, err := SuccessResponse(status, body, metadata)

var responseBody SuccessResponseBody
json.Unmarshal([]byte(response.Body), &responseBody)

metadataStr, _ := json.Marshal(responseBody.Metadata)
var metadataRes PaginationMetadata
json.Unmarshal([]byte(metadataStr), &metadataRes)

resultsStr, _ := json.Marshal(responseBody.Results)
var resultsRes []map[string]string
json.Unmarshal([]byte(resultsStr), &resultsRes)

assert.ShouldBeNil(t, err)

assert.DeepEqual(t, metadataRes, metadata, "invalid metadata")
assert.DeepEqual(t, resultsRes[0], body[0], "invalid value first element")
assert.DeepEqual(t, resultsRes[1], body[1], "invalid value for second element")

assert.Equal(t, response.StatusCode, status, "invalid status code")
}

func ExampleSuccessResponse() {
status := http.StatusOK

singleBody := map[string]string{
"naknang": "sonof",
"patatas": "potato",
}

singleResponse, _ := SuccessResponse(status, singleBody)

multipleBody := []map[string]string{
{"naknang": "sonof", "patatas": "potato"},
{"sonof": "naknang", "potato": "patatas"},
}

metadata := PaginationMetadata{
PageCount: 10,
ResultCount: 100,
}

multiResponse, _ := SuccessResponse(status, multipleBody, metadata)

var responseBody SuccessResponseBody
json.Unmarshal([]byte(multiResponse.Body), &responseBody)

metadataStr, _ := json.Marshal(responseBody.Metadata)
var metadataRes PaginationMetadata
json.Unmarshal([]byte(metadataStr), &metadataRes)

resultsStr, _ := json.Marshal(responseBody.Results)
var resultsRes []map[string]string
json.Unmarshal([]byte(resultsStr), &resultsRes)

fmt.Println(singleResponse.Body, singleResponse.StatusCode)
fmt.Println(multiResponse.Body, multiResponse.StatusCode)

// Output:
// {"naknang":"sonof","patatas":"potato"} 200
// {"results":[{"naknang":"sonof","patatas":"potato"},{"potato":"patatas","sonof":"naknang"}],"metadata":{"max_page":10,"results_per_page":100}} 200
}
12 changes: 12 additions & 0 deletions assert/deep_equal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package assert

import (
"reflect"
"testing"
)

func DeepEqual(t *testing.T, got any, want any, notes string) {
if !reflect.DeepEqual(got, want) {
t.Errorf("%s got <%s> wanted <%s>", notes, got, want)
}
}
9 changes: 9 additions & 0 deletions assert/equal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package assert

import "testing"

func Equal(t *testing.T, got any, want any, notes string) {
if got != want {
t.Errorf("%s got <%s> wanted <%s>", notes, got, want)
}
}
3 changes: 3 additions & 0 deletions assert/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/scrambledeggs/booky-go-common/assert

go 1.21.1
Loading

0 comments on commit 7aa1b75

Please sign in to comment.