aws / aws-sdk-js

AWS SDK for JavaScript in the browser and Node.js
https://aws.amazon.com/developer/language/javascript/
Apache License 2.0
7.59k stars 1.55k forks source link

Lambda.invoke() doesn't send client context #1388

Closed ghost closed 7 years ago

ghost commented 7 years ago

Im using the latest Javascript SDK and trying to send some client context from a browser app to a Lambda function. Here's a snippet of the sending end:

AWS.config.apiVersions = { lambda: '2015-03-31' };
var la = new AWS.Lambda();
var upevent = { a: 'something' };
var ctx = { LastSequence: lastSequence == null ? '0': lastSequence };
var b64ctx = btoa(JSON.stringify(ctx));
var params = {
    FunctionName: CREATE_EVENT,
    ClientContext: b64ctx,
    InvocationType: 'RequestResponse',
    LogType: 'None',
    Payload: JSON.stringify(upevent)
};
la.invoke(params, function(err, data) {
    ...

This call completes fine and even returns the data I want it to. However the Client Context is lost. On the server side, I have a Python Lambda function with the usual method signature:

def lambda_handler(event, context):
    ctx = context.client_context

Upon execution the 'ctx' object has three attributes: client, custom, env all of which are None.

Where is my 'client context'?

I'm pretty sure it's being sent correctly on the client side, as if you pass a string that isn't correct, then you get a message 'Client context must be a valid Base64-encoded JSON object'.

jeskew commented 7 years ago

It looks like you need to put the data you wish to send under the key custom in the object that gets JSON.stringify'd and base64 encoded. For example, with the following client code:

var AWS = require('aws-sdk');
var la = new AWS.Lambda({ region: 'us-west-2' });
var ctx = {
    custom: { foo: 'bar' },
    client: { snap: ['crackle', 'pop']},
    env: { fizz: 'buzz' },
};
la.invoke({
    FunctionName: 'contextPrinter',
    ClientContext: AWS.util.base64.encode(JSON.stringify(ctx)),
    InvocationType: 'RequestResponse',
    Payload: JSON.stringify({ baz: 'quux' })
}, function (err, data) { return console.log(err, data); });

and the following lambda:

def lambda_handler(event, context):
    print("context", context.client_context)
    print("client", ', '.join(i for i in dir(context.client_context.client) if not i.startswith('__')))
    print("custom", context.client_context.custom)
    print("env", context.client_context.env)

I get the following log output:

('context', <__main__.ClientContext object at 0x7f5705898170>)
('client', 'app_package_name, app_title, app_version_code, app_version_name, installation_id')
('custom', {u'foo': u'bar'})
('env', {u'fizz': u'buzz'})

The client context contains data about the client, but data under the custom or env keys is made available to the lambda function.

ghost commented 7 years ago

Thanks Jason, I can verify that that works now. This should be clarified in the documentation - how do we get an update request into the doc team?

smber1 commented 6 years ago

The clientContext will also only be passed to the invoked lambda if the InvocationType is RequestResponse. Passing Event as the InvocationType (for fire & forget/async invocations) seems to suppress the ClientContext for reasons unbeknownst to me! Perhaps the docs should be updated to relate this?

Aea commented 6 years ago

I can confirm @smber1's observation, not getting passed along when running as Event, fine when running as RequestResponse, my sample payload was: eyJjdXN0b20iOnsidXVpZCI6IjA5MGI4ZWJiLWM0NjAtNGZiZS04ZDM0LTI5NWFkZmVjYmE4OCIsInNoYXJkIjowfX0=

Personally I'm finding the documentation around this to be quite sparse, it seems like clientContext is really intended to be used with their mobile / ad SDKs and not well supported otherwise.

toritsejuFO commented 1 year ago

Don't know if there's an update on this by this time. Would love to pass some context from an ending function call into a chained invocation to maintain state until long-running processing job is complete.