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

Null safety migration #26

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ doc/api/
# Further artifacts
*.exe
*.zip
bootstrap
bootstrap

.idea
File renamed without changes.
12 changes: 6 additions & 6 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import 'package:aws_lambda_dart_runtime/aws_lambda_dart_runtime.dart';
import 'package:aws_lambda_dart_runtime/runtime/context.dart';

void main() async {
/// This demo's handling an API Gateway request.
final Handler<AwsApiGatewayEvent> helloApiGateway = (context, event) async {
final response = {"message": "hello ${context.requestId}"};
final helloApiGateway = (Context context, AwsApiGatewayEvent event) async {
final response = {'message': 'hello ${context.requestId}'};

/// it returns an encoded response to the gateway
return InvocationResult(
context.requestId, AwsApiGatewayResponse.fromJson(response));
/// it returns an response to the gateway
return AwsApiGatewayResponse.fromJson(response);
};

/// The Runtime is a singleton. You can define the handlers as you wish.
Runtime()
..registerHandler<AwsApiGatewayEvent>("hello.apigateway", helloApiGateway)
..registerHandler<AwsApiGatewayEvent>('hello.apigateway', helloApiGateway)
..invoke();
}
4 changes: 2 additions & 2 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: example

environment:
sdk: ">=2.6.0 <3.0.0"
sdk: ">=2.12.0 <3.0.0"

dependencies:
aws_lambda_dart_runtime:
git: https://github.com/awslabs/aws-lambda-dart-runtime.git
path: ..

dev_dependencies:
build_runner:
109 changes: 49 additions & 60 deletions lib/client/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'dart:io';
import 'dart:async';
import 'dart:convert';

import 'package:http/http.dart' as http;

/// Next invocation data wraps the data from the
/// invocation endpoint of the Lambda Runtime Interface.
class NextInvocation {
Expand All @@ -20,43 +22,41 @@ class NextInvocation {
final String requestId;

/// Deadline milliseconds is the setting for ultimate cancelation of the invocation.
final String deadlineMs;
final String? deadlineMs;

/// Invoked function ARN is the identifier of the function.
final String invokedFunctionArn;
final String? invokedFunctionArn;

/// Tracing id is the identifier for tracing like X-Ray.
final String traceId;
final String? traceId;

/// Client context is the context that is provided to the function.
final String clientContext;
final String? clientContext;

/// Cognito identity is the identity that maybe is used for authorizing the request.
final String cognitoIdentity;
final String? cognitoIdentity;

/// Digesting a [HttpClientResponse] into a [NextInvocation].
static Future<NextInvocation> fromResponse(
HttpClientResponse response) async {
return NextInvocation(
response: json
.decode((await response.transform(Utf8Decoder()).toList()).first),
requestId: response.headers.value(runtimeRequestId),
deadlineMs: response.headers.value(runtimeDeadlineMs),
invokedFunctionArn: response.headers.value(runtimeInvokedFunctionArn),
traceId: response.headers.value(runtimeTraceId),
clientContext: response.headers.value(runtimeClientContext),
cognitoIdentity: response.headers.value(runtimeCognitoIdentity));
}

const NextInvocation(
{this.requestId,
this.deadlineMs,
this.traceId,
this.clientContext,
this.cognitoIdentity,
this.invokedFunctionArn,
this.response})
: assert(requestId != null);
static Future<NextInvocation> fromResponse(http.Response response) async =>
NextInvocation(
response: (json.decode(utf8.decode(response.bodyBytes)) as Map)
.cast<String, dynamic>(),
requestId: response.headers[runtimeRequestId]!,
deadlineMs: response.headers[runtimeDeadlineMs],
invokedFunctionArn: response.headers[runtimeInvokedFunctionArn],
traceId: response.headers[runtimeTraceId],
clientContext: response.headers[runtimeClientContext],
cognitoIdentity: response.headers[runtimeCognitoIdentity]);

const NextInvocation({
required this.requestId,
this.deadlineMs,
this.traceId,
this.clientContext,
this.cognitoIdentity,
this.invokedFunctionArn,
required this.response,
});
}

/// Invocation result is the result that the invoked handler
Expand All @@ -71,9 +71,7 @@ class InvocationResult {
/// any json-encodable data type.
final dynamic body;

const InvocationResult(this.requestId, this.body)
: assert(requestId != null),
assert(body != null);
const InvocationResult(this.requestId, this.body) : assert(body != null);
}

/// Invocation error occurs when there has been an
Expand All @@ -92,8 +90,7 @@ class InvocationError {
/// representation for the Runtime Interface.
Map<String, dynamic> toJson() => {
'errorMessage': error.toString(),
'errorType': "InvocationError",
'stackTrace': this.stackTrace.toString()
'errorType': 'InvocationError',
};

const InvocationError(this.error, this.stackTrace);
Expand All @@ -103,7 +100,7 @@ class InvocationError {
/// It is implemented as a singleton whereby [Client.instance]
/// always returns the already instantiated client.
class Client {
HttpClient _client;
late http.Client _client;

static final Client _singleton = Client._internal();

Expand All @@ -112,53 +109,45 @@ class Client {
}

Client._internal() {
_client = HttpClient();
_client = http.Client();
}

static const runtimeApiVersion = '2018-06-01';
static final runtimeApi = Platform.environment["AWS_LAMBDA_RUNTIME_API"];

/// Get the next inovation from the AWS Lambda Runtime Interface (see https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html).
static String? get runtimeApi =>
Platform.environment['AWS_LAMBDA_RUNTIME_API'];

static String? get handlerName => Platform.environment['_HANDLER'];

/// Get the next invocation from the AWS Lambda Runtime Interface (see https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html).
Future<NextInvocation> getNextInvocation() async {
final request = await _client.getUrl(Uri.parse(
'http://${runtimeApi}/${runtimeApiVersion}/runtime/invocation/next'));
final response = await request.close();
final response = await _client.get(Uri.parse(
'http://$runtimeApi/$runtimeApiVersion/runtime/invocation/next'));
return NextInvocation.fromResponse(response);
}

/// Post the invocation response to the AWS Lambda Runtime Interface.
Future<HttpClientResponse> postInvocationResponse(
InvocationResult result) async {
final request = await _client.postUrl(
Future<http.Response> postInvocationResponse(
String? requestId, dynamic payload) async {
return await _client.post(
Uri.parse(
'http://${runtimeApi}/${runtimeApiVersion}/runtime/invocation/${result.requestId}/response',
'http://$runtimeApi/$runtimeApiVersion/runtime/invocation/$requestId/response',
),
body: jsonEncode(payload),
);
request.add(
utf8.encode(
json.encode(result.body),
),
);

return await request.close();
}

/// Post an invocation error to the AWS Lambda Runtime Interface.
/// It takes in an [InvocationError] and the [requestId]. The [requestId]
/// is used to map the error to the execution.
Future<HttpClientResponse> postInvocationError(
Future<http.Response> postInvocationError(
String requestId, InvocationError err) async {
final request = await _client.postUrl(
return await _client.post(
Uri.parse(
'http://${runtimeApi}/${runtimeApiVersion}/runtime/invocation/$requestId/error',
'http://$runtimeApi/$runtimeApiVersion/runtime/invocation/$requestId/error',
),
body: jsonEncode(err),
headers: {'Content-type': 'application/json'},
);
request.add(
utf8.encode(
json.encode(err)
)
);

return await request.close();
}
}
65 changes: 36 additions & 29 deletions lib/events/alb_event.dart
Original file line number Diff line number Diff line change
@@ -1,77 +1,79 @@
import 'dart:io';

import 'package:aws_lambda_dart_runtime/runtime/event.dart';
import 'package:json_annotation/json_annotation.dart';

part 'alb_event.g.dart';

/// Event send by an Application Load Balancer to the
/// invocation to the Lambda.
@JsonSerializable()
class AwsALBEvent {
class AwsALBEvent extends Event {
/// Request context in which this request is executed.
/// For the ELB this is the ARN of the target group.
@JsonKey()
final AwsALBEventContext context;
final AwsALBEventContext? context;

/// HTTP method that is used to trigger the invocation of the Lambda.
@JsonKey()
final String httpMethod;
final String? httpMethod;

/// The URI that is accessed to trigger the invocation of the Lambda.
@JsonKey()
final String path;
final String? path;

/// HTTP headers that are send with the request to the load balancer.
@JsonKey()
final Map<String, dynamic> headers;
final Map<String, dynamic>? headers;

/// The query parameters for the request to the load balancer.
@JsonKey()
final Map<String, dynamic> queryStringParameters;
final Map<String, dynamic>? queryStringParameters;

/// Body of the request. This can be data that is send with the POST
/// to the request.
@JsonKey()
final String body;
final String? body;

/// Singals that the request is Base64 encoded.
@JsonKey()
final bool isBase64Encoded;
final bool? isBase64Encoded;

factory AwsALBEvent.fromJson(Map<String, dynamic> json) =>
_$AwsALBEventFromJson(json);

Map<String, dynamic> toJson() => _$AwsALBEventToJson(this);

const AwsALBEvent(
{this.context,
this.httpMethod,
this.path,
this.headers,
this.queryStringParameters,
this.body,
this.isBase64Encoded});
const AwsALBEvent({
this.context,
this.httpMethod,
this.path,
this.headers,
this.queryStringParameters,
this.body,
this.isBase64Encoded,
});
}

/// Response for a request from an Application Load Balancer.
/// It has to have a [statusCode], [headers] and a [body].
/// They should reflect the informationen needed here.
class AwsALBResponse {
/// The body of the HTTP Response send from the API Gateway to the client.
String body;
String? body;

/// Indicates if the [body] is Base64 encoded or not. By default is `false`.
bool isBase64Encoded;
bool? isBase64Encoded;

/// HTTP status code of the response of the API Gateway to the client.
/// The default status code is `200 OK`.
int statusCode;
int? statusCode;

/// Description of the send HTTP status code.
String statusDescription;
String? statusDescription;

/// The HTTP headers that should be send with the response to the client.
Map<String, String> headers;
Map<String, String>? headers;

/// Returns the JSON representation of the response. This is called by
/// the JSON encoder to produce the response.
Expand All @@ -85,10 +87,10 @@ class AwsALBResponse {

factory AwsALBResponse.fromString(
String body, {
bool isBase64Encoded,
int statusCode,
String statusDescription,
Map<String, String> headers,
bool? isBase64Encoded,
int? statusCode,
String? statusDescription,
Map<String, String>? headers,
}) {
return AwsALBResponse(
body: body,
Expand All @@ -100,13 +102,18 @@ class AwsALBResponse {

/// The Response that should be returned to the Application Load Balancer.
/// It is constructed with some default values for the optional parameters.
AwsALBResponse(
{body, headers, isBase64Encoded, statusCode, statusDescription}) {
AwsALBResponse({
String? body,
Map<String, String>? headers,
bool? isBase64Encoded,
int? statusCode,
String? statusDescription,
}) {
this.body = body ?? '';
this.isBase64Encoded = isBase64Encoded ?? false;
this.headers = headers ?? {"Content-Type": "text/html; charset=utf-8"};
this.headers = headers ?? {'Content-Type': 'text/html; charset=utf-8'};
this.statusCode = statusCode ?? HttpStatus.ok;
this.statusDescription = statusDescription ?? "200 OK";
this.statusDescription = statusDescription ?? '200 OK';
}
}

Expand Down
12 changes: 6 additions & 6 deletions lib/events/alb_event.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading