Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plumb JSON Decoder options #515

Merged
merged 5 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions lambda/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ type Handler interface {

type handlerOptions struct {
handlerFunc
baseContext context.Context
jsonResponseEscapeHTML bool
jsonResponseIndentPrefix string
jsonResponseIndentValue string
enableSIGTERM bool
sigtermCallbacks []func()
baseContext context.Context
jsonRequestUseNumber bool
jsonRequestDisallowUnknownFields bool
jsonResponseEscapeHTML bool
jsonResponseIndentPrefix string
jsonResponseIndentValue string
enableSIGTERM bool
sigtermCallbacks []func()
}

type Option func(*handlerOptions)
Expand Down Expand Up @@ -81,6 +83,38 @@ func WithSetIndent(prefix, indent string) Option {
})
}

// WithUseNumber sets the UseNumber option on the underlying json decoder
//
// Usage:
//
// lambda.StartWithOptions(
// func (event any) (any, error) {
// return event, nil
// },
// lambda.WithUseNumber(true)
// )
func WithUseNumber(useNumber bool) Option {
return Option(func(h *handlerOptions) {
h.jsonRequestUseNumber = useNumber
})
}

// WithUseNumber sets the DisallowUnknownFields option on the underlying json decoder
//
// Usage:
//
// lambda.StartWithOptions(
// func (event any) (any, error) {
// return event, nil
// },
// lambda.WithDisallowUnknownFields(true)
// )
func WithDisallowUnknownFields(disallowUnknownFields bool) Option {
return Option(func(h *handlerOptions) {
h.jsonRequestDisallowUnknownFields = disallowUnknownFields
})
}

// WithEnableSIGTERM enables SIGTERM behavior within the Lambda platform on container spindown.
// SIGKILL will occur ~500ms after SIGTERM.
// Optionally, an array of callback functions to run on SIGTERM may be provided.
Expand Down Expand Up @@ -267,6 +301,12 @@ func reflectHandler(f interface{}, h *handlerOptions) handlerFunc {
out.Reset()
in := bytes.NewBuffer(payload)
decoder := json.NewDecoder(in)
if h.jsonRequestUseNumber {
decoder.UseNumber()
}
if h.jsonRequestDisallowUnknownFields {
decoder.DisallowUnknownFields()
}
encoder := json.NewEncoder(out)
encoder.SetEscapeHTML(h.jsonResponseEscapeHTML)
encoder.SetIndent(h.jsonResponseIndentPrefix, h.jsonResponseIndentValue)
Expand Down
49 changes: 49 additions & 0 deletions lambda/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"io"
"io/ioutil" //nolint: staticcheck
"reflect"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -309,6 +310,54 @@ func TestInvokes(t *testing.T) {
},
options: []Option{WithSetIndent(">>", " ")},
},
{
name: "WithUseNumber(true) results in json.Number instead of float64 when decoding to an interface{}",
input: `19.99`,
expected: expected{`"Number"`, nil},
handler: func(event interface{}) (string, error) {
return reflect.TypeOf(event).Name(), nil
},
options: []Option{WithUseNumber(true)},
},
{
name: "WithUseNumber(false)",
input: `19.99`,
expected: expected{`"float64"`, nil},
handler: func(event interface{}) (string, error) {
return reflect.TypeOf(event).Name(), nil
},
options: []Option{WithUseNumber(false)},
},
{
name: "No decoder options provided is the same as WithUseNumber(false)",
input: `19.99`,
expected: expected{`"float64"`, nil},
handler: func(event interface{}) (string, error) {
return reflect.TypeOf(event).Name(), nil
},
options: []Option{},
},
{
name: "WithDisallowUnknownFields(true)",
input: `{"Hello": "World"}`,
expected: expected{"", errors.New(`json: unknown field "Hello"`)},
handler: func(_ struct{}) {},
options: []Option{WithDisallowUnknownFields(true)},
},
{
name: "WithDisallowUnknownFields(false)",
input: `{"Hello": "World"}`,
expected: expected{`null`, nil},
handler: func(_ struct{}) {},
options: []Option{WithDisallowUnknownFields(false)},
},
{
name: "No decoder options provided is the same as WithDisallowUnknownFields(false)",
input: `{"Hello": "World"}`,
expected: expected{`null`, nil},
handler: func(_ struct{}) {},
options: []Option{},
},
{
name: "bytes are base64 encoded strings",
input: `"aGVsbG8="`,
Expand Down