awslabs / aws-lambda-dart-runtime Goto Github PK
View Code? Open in Web Editor NEWA Dart runtime for AWS Lambda
Home Page: https://awslabs.github.io/aws-lambda-dart-runtime/
License: Apache License 2.0
A Dart runtime for AWS Lambda
Home Page: https://awslabs.github.io/aws-lambda-dart-runtime/
License: Apache License 2.0
Would be great to CDK support and examples in the readme on how to use this with CDK.
The runtime's invoke
function contains an exception catcher while getting the next invocation from AWS API. It goes like this,
} on Exception catch (error, stacktrace) {
await _client.postInvocationError(
nextInvocation.requestId, InvocationError(error, stacktrace));
}
The code assumes that nextInvocation
is never null and contains the requestId
. But there are certain sceranios where the nextInvocation
is null. One of them is when the HTTP request inside the _client.getInvocation
function throws an HttpException: Connection closed before full header was received
exception.
// inside invoke function's try closure
nextInvocation = await _client.getNextInvocation();
// inside the `getNextInvocation` function
final request = await _client.getUrl(Uri.parse(
'http://${runtimeApi}/${runtimeApiVersion}/runtime/invocation/next'));
final response = await request.close();
return NextInvocation.fromResponse(response);
The error is reproducible and happens on multiple & regular occasions. When it happens, the requestId
is being called on null and throws a process exception and never ends the request. But it could be easily solved by checking whether the nextInvocation
is null or not.
} on Exception catch (error, stacktrace) {
if (nextInvocation != null) {
await _client.postInvocationError(
nextInvocation.requestId, InvocationError(error, stacktrace));
}
}
I'll submit a PR.
I think the AwsApiGatewayResponse is missing the ability to set headers.
Adding a Map<String, String> headers
or Map<String, dynamic> headers
to this class should do the trick!
When there is no registered handler(or e.g. misspelled handler) this error is printed:
Unhandled exception:
NoSuchMethodError: The getter 'type' was called on null.
Receiver: null
Tried calling: type
#0 Runtime.invoke (package:aws_lambda_dart_runtime/runtime/runtime.dart:107)
It is a little bit confusing and hard to track down the cause(no registered handler by that name).
I am trying to follow api gateway example in readme
My code looks like
import 'package:aws_lambda_dart_runtime/aws_lambda_dart_runtime.dart';
void main() async {
print("hello from inside dart 1");
/// This demo's handling an API Gateway request.
final Handler<AwsApiGatewayEvent> helloApiGateway = (context, event) async {
print("hello from inside dart 2");
final response = {"message": "hello ${context.requestId}"};
print("hello from inside dart 3");
/// it returns an encoded response to the gateway
final resp = InvocationResult(
context.requestId, AwsApiGatewayResponse.fromJson(response));
print("hello from inside dart 4");
return resp;
};
/// The Runtime is a singleton. You can define the handlers as you wish.
Runtime()
..registerHandler<AwsApiGatewayEvent>("main.hello", helloApiGateway)
..invoke();
}
When endpoint invoked via curl, I get
{"message": "Internal server error"}
but I see all hello...
prints in the lambda cloudwatch logs and request end without any exception.
When enpoint is invoked using AWS console for apigateway test UI, I see
Received response. Status: 200, Integration latency: 24 ms
Thu May 27 23:15:24 UTC 2021 : Endpoint response headers: {Date=Thu, 27 May 2021 23:15:24 GMT, Content-Type=application/json, Content-Length=128, Connection=keep-alive, x-amzn-RequestId=d71971ef-c9f7-4999-a306-2cec2aee846d, X-Amz-Function-Error=Unhandled, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-60b0280c-20ab219fdb20cbc96a699b53;sampled=0}
Thu May 27 23:15:24 UTC 2021 : Endpoint response body before transformations: {"errorMessage":"Converting object to an encodable object failed: Instance of 'InvocationResult'","errorType":"InvocationError"}
Thu May 27 23:15:24 UTC 2021 : Lambda execution failed with status 200 due to customer function error: Converting object to an encodable object failed: Instance of 'InvocationResult'. Lambda request id: d71971ef-c9f7-4999-a306-2cec2aee846d
Thu May 27 23:15:24 UTC 2021 : Method completed with status: 502
I tried changing line
return resp;
to ut8.encode(json.encode(resp))
and a few other things. No luck.
I am not sure what to do. Any help.
I am deploying using serverless.
I had to set the runtime as provided.al2
since it would not do it with runtime as dart
Is there anything on the roadmap to migrate this package to null safety? ๐
This is really amazing for the server side. Congrats to AWS for finally acknowledging the existence of Flutter. However, for AWS to really be able to compete with Firebase in Flutter, you need a few more things in the CLIENT-SIDE:
โข Auth (Cognito) โ So that the lambda can authenticate the users.
โข An easier way to send/receive Json to Lambda (a few functions to use instead of forcing people to manually use https).
โข AppSync โ This one is the most important for you to compete with Firebase. We need realtime. It may use Platform Channels, but it would be perfect if it were all done in Dart.
โข An easier way to send files to S3. (a few functions to use instead of forcing people to manually use https).
This is mentioned in the readme document's "Future & Ideas" section.
What is necessary to make this a reality?
It is a bit confusing that all constructor parameters is dynamic
, when supplying the wrong type will throw a runtime error.
AwsApiGatewayResponse({
body,
isBase64Encoded,
headers,
statusCode,
}) {
this.body = body ?? '';
this.isBase64Encoded = isBase64Encoded ?? false;
this.headers = headers ?? {"Content-Type": "application/json"};
this.statusCode = statusCode ?? HttpStatus.ok;
}
It would make the API more friendly and less error prone if the parameters had types:
AwsApiGatewayResponse({
String body,
bool isBase64Encoded,
Map<String, String> headers,
int statusCode,
}) {
this.body = body ?? '';
this.isBase64Encoded = isBase64Encoded ?? false;
this.headers = headers ?? {"Content-Type": "application/json"};
this.statusCode = statusCode ?? HttpStatus.ok;
}
First of all THANKYOU for such amazing repository.
There is any reason for why this project isn't listed on pub.dev?
Pub is the dart's official package repository and is often used by other companies to search for the capabilities of a language. Having an AWS Lambda support there would be a great showcase for the language.
Hello, I use this package and would be happy to help maintain it and publish updates to pub.dev. @katallaxie if you are no longer able or willing to work on this package could you please add me as a contributor here on github and pub.dev?
I am, not just unit testing, but the app itself. When I try to run it I get the error:
Unhandled exception:
NoSuchMethodError: The getter 'requestId' was called on null.
Receiver: null
Tried calling: requestId
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 Runtime.invoke (package:aws_lambda_dart_runtime/runtime/runtime.dart:117:28)
<asynchronous suspension>
#2 main (file:///C:/Users/shina/Desenvolvimento/dart_projects/lambda_test/bin/lambda_test.dart:62:7)
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
There is a way to run it (for testing purposes) locally, without having to upload the zip file to aws?
When the runtime tries posting an invocation error I got this
Unhandled exception:
type '_InternalLinkedHashMap<String, String>' is not a subtype of type 'String'
#0 Client.postInvocationError (package:aws_lambda_dart_runtime/client/client.dart:156)
#1 _RootZone.runUnary (dart:async/zone.dart:1381)
#2 _FutureListener.handleValue (dart:async/future_impl.dart:139)
#3 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:680)
#4 Future._propagateToListeners (dart:async/future_impl.dart:709)
#5 Future._completeWithValue (dart:async/future_impl.dart:524)
#6 Future._asyncComplete.<anonymous closure> (dart:async/future_impl.dart:554)
#7 _microtaskLoop (dart:async/schedule_microtask.dart:43)
#8 _startMicrotaskLoop (dart:async/schedule_microtask.dart:52)
#9 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:393)
#10 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:418)
#11 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174)
When I changed this line:
request.add(utf8.encode(jsonEncode(err.error)));
It got past that line. If the error object has a toJson()
(which is not guaranteed), customary in Dart is to return Map<String, dynamic>
, which is not what utf8.encode()
expects.
I think a more stable solution is to simply call toString()
on the error object.
This package has been working great for me in prod! I ran into a use case today that I thought could be improvd. AwsApiGatewayEventHeaders is parsing some standard headers like userAgent
. This is great however I can not get access to custom headers or even simple caching headers like If-None-Match
.
Would it be possible to add raw
or rawHeaders
(naming is hard) to AwsApiGatewayEventHeaders
? This would expose a way to access any possible header that comes through while keeping the existing functionality.
Example:
event.headers.raw['x-my-custom-header']
or
event.headers.raw.contains('x-my-custom-header')
raw
here being the entire headers map.
Thanks for the quick fix on the last issue! #5
I found another one. AwsApiGatewayEvent is missing body
in the event of a POST
or PUT
. Looks like your test json you only tested for a GET
. I'd assume this could be null
in the event of a GET
and a string otherwise.
Hey guys, I am trying to develop my first function using this dart runtime but I my tests giving this error:
Response:
{
"errorMessage": "RequestId: a81f0097-a81f-48ac-88e7-cdecf090bc91 Error: fork/exec /var/task/bootstrap: permission denied",
"errorType": "Runtime.InvalidEntrypoint"
}
Request ID:
"a81f0097-a81f-48ac-88e7-cdecf090bc91"
Function logs:
START RequestId: a81f0097-a81f-48ac-88e7-cdecf090bc91 Version: $LATEST
END RequestId: a81f0097-a81f-48ac-88e7-cdecf090bc91
REPORT RequestId: a81f0097-a81f-48ac-88e7-cdecf090bc91 Duration: 0.88 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 6 MB
RequestId: a81f0097-a81f-48ac-88e7-cdecf090bc91 Error: fork/exec /var/task/bootstrap: permission denied
Runtime.InvalidEntrypoint
The weird stuff is that in my console, it shows a tab with 'null' title on it with a spinner that never stops spinning:
This is my dart code:
import 'package:aws_lambda_dart_runtime/aws_lambda_dart_runtime.dart';
void main() async {
print('STARTING THIS LAMDBA TEST IN DART!');
/// This demo's handling an API Gateway request.
final helloApiGateway = (context, event) async {
final response = {
'message': 'hello ${context.requestId}',
};
/// it returns an encoded response to the gateway
return InvocationResult(
context.requestId,
AwsApiGatewayResponse.fromJson(response),
);
};
/// The Runtime is a singleton. You can define the handlers as you wish.
Runtime()
..registerHandler<AwsApiGatewayEvent>('hello.apigateway', helloApiGateway)
..invoke();
}
And this is the test event I am using:
{
"requestContext": {
"elb": {
"targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a"
}
},
"httpMethod": "GET",
"path": "/lambda",
"queryStringParameters": {
"query": "1234ABCD"
},
"headers": {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip",
"accept-language": "en-US,en;q=0.9",
"connection": "keep-alive",
"host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
"x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476",
"x-forwarded-for": "72.12.164.125",
"x-forwarded-port": "80",
"x-forwarded-proto": "http",
"x-imforwards": "20"
},
"body": "",
"isBase64Encoded": false
}
This event test can, maybe, be causing this error?
Check this for the parsing of the AwsAlexaEvent
It is expecting and header and a payload object.
The problem is, when we are testing the lambdas on aws, this is the json provided by the lambda testing tool:
{
"version": "1.0",
"session": {
"new": true,
"sessionId": "amzn1.echo-api.session.123456789012",
"application": {
"applicationId": "amzn1.ask.skill.987654321"
},
"user": {
"userId": "amzn1.ask.account.testUser"
},
"attributes": {}
},
"context": {
"AudioPlayer": {
"playerActivity": "IDLE"
},
"System": {
"application": {
"applicationId": "amzn1.ask.skill.987654321"
},
"user": {
"userId": "amzn1.ask.account.testUser"
},
"device": {
"supportedInterfaces": {
"AudioPlayer": {}
}
}
}
},
"request": {
"type": "LaunchRequest",
"requestId": "amzn1.echo-api.request.1234",
"timestamp": "2016-10-27T18:21:44Z",
"locale": "en-US"
}
}
As you can see there is no header
or payload
objects on this JSON, with ends by returning an empty AwsAlexaEvent.
The whole program has to be instrumented with async/await. As everything must be waited for to properly complete before your lambda function handler returns. No events can be left on the event queue when your handler returns or they will non-deterministically fail.
AWS freezes the lambda process between lambda calls (which it identify is over when you return a response from your handler). Anything left going after that in the background will see a timejump to when the lambda is called next, and will see its in-use connections reset.
Ideally the framework would wait until the event queue is empty (remove the frameworks own events). But dart might not expose the ability to do this in its own API. So perhaps just line in the documentation to warn about this issue.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.