-
Notifications
You must be signed in to change notification settings - Fork 573
Allow ClientContext.Custom unmarshaling for non-string (JSON) values #620
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ package lambdacontext | |
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "os" | ||
| "strconv" | ||
| ) | ||
|
|
@@ -68,6 +69,35 @@ type ClientContext struct { | |
| Custom map[string]string `json:"custom"` | ||
| } | ||
|
|
||
| // UnmarshalJSON implements custom JSON unmarshaling for ClientContext. | ||
| // This handles the case where values in the "custom" map are not strings | ||
| // (e.g. nested JSON objects), by serializing non-string values back to | ||
| // their JSON string representation. | ||
| func (cc *ClientContext) UnmarshalJSON(data []byte) error { | ||
| var raw struct { | ||
| Client ClientApplication `json:"Client"` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
nevermind
|
||
| Env map[string]string `json:"env"` | ||
| Custom map[string]json.RawMessage `json:"custom"` | ||
| } | ||
| if err := json.Unmarshal(data, &raw); err != nil { | ||
| return err | ||
| } | ||
| cc.Client = raw.Client | ||
| cc.Env = raw.Env | ||
| if raw.Custom != nil { | ||
| cc.Custom = make(map[string]string, len(raw.Custom)) | ||
| for k, v := range raw.Custom { | ||
| var s string | ||
| if err := json.Unmarshal(v, &s); err == nil { | ||
| cc.Custom[k] = s | ||
| } else { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the nested json in the Client Context like, critical to all use of the Bringing this option up, so as not to be hasty in type coercion decisions that could introduce inconsistencies with other runtimes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate on why you would drop types other than a string? It seems possible that this would bite you down the road if another service was enabled that needed this but had values other than strings.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Balancing urgency in (partially?) unblocking a use-case, and introducing warts that'd be hard to undo without semver bumps. Other runtimes have the same blocker, and it'd be prudent to line up a similar strategy for supporting the usecase. For Go, other options that solve for making the context parse more lenient, without committing to inconsistent type coercion for this novel use of client context:
An option to make the novel use of Client Context readable in a way consistent with node, python, (eg: no type coercion) could be something like stuffing the and adding new function ^ is my general recommendation to keep back compat while also unblocking the new use-case. But given that .NET and Java are already inconsistent this isn't necessarily the only solution. |
||
| cc.Custom[k] = string(v) | ||
M-Elsaeed marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // CognitoIdentity is the cognito identity used by the calling application. | ||
| type CognitoIdentity struct { | ||
| CognitoIdentityID string | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package lambdacontext | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestClientContextUnmarshalJSON(t *testing.T) { | ||
| t.Run("non-string custom values are serialized to string", func(t *testing.T) { | ||
| input := `{ | ||
| "Client": {"installation_id": "install1"}, | ||
| "custom": { | ||
| "key1": "stringval", | ||
| "key2": {"nested": "object"}, | ||
| "key3": 42 | ||
| } | ||
| }` | ||
| var cc ClientContext | ||
| err := json.Unmarshal([]byte(input), &cc) | ||
| require.NoError(t, err) | ||
| assert.Equal(t, "install1", cc.Client.InstallationID) | ||
| assert.Equal(t, "stringval", cc.Custom["key1"]) | ||
| assert.JSONEq(t, `{"nested":"object"}`, cc.Custom["key2"]) | ||
| assert.Equal(t, "42", cc.Custom["key3"]) | ||
| }) | ||
|
|
||
| t.Run("invalid JSON returns error", func(t *testing.T) { | ||
| var cc ClientContext | ||
| err := json.Unmarshal([]byte(`not valid json`), &cc) | ||
| assert.Error(t, err) | ||
| }) | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking there may need to be a
MarshalJSONtoo, so that a re-serialization produces the same output as what was input. (number, bool values. quote escaping in nested json)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see: https://go.dev/play/p/8q6bgWa-Rcw where re marshaling results in a type change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting. Java and .NET already be mangling this with string conversions for numbers and bools. And .NET only will stringify nested values.
Client Context
custom— Return Value Test MatrixAll functions return
context.clientContext.customas JSON.Test 1:
{"custom": {"number": 9001, "bool": false}}{"number":"9001","bool":"false"}{"number":"9001","bool":"False"}invalid type: integer '9001', expected a string)cannot unmarshal number into Go struct field ClientContext.custom of type stringTest 2:
{"custom": {"nested": {"hello": "world"}}}Expected a string but was BEGIN_OBJECT{"nested":"{\"hello\":\"world\"}"}invalid type: map, expected a string)cannot unmarshal object into Go struct field ClientContext.custom of type string