Skip to content
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
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,24 @@ class Greet extends HttpApi {

The request context is passed into a request value named *request* and contains a [RequestContext instance](https://github.com/xp-forge/lambda-ws#request-context). The [lambda context](https://github.com/xp-forge/lambda#context) is passed in *context*.

To run existing web applications, return an instance of your application subclass from the *routes()* method. This way, you can also test them locally using the `xp web` command.
To run existing web applications, return an instance of your `web.Application` subclass from the *routes()* method.

Development & testing
---------------------
To run the HTTP APIs locally, this library integrates with [xp-forge/web](https://github.com/xp-forge/web) via a wrapper:

```bash
$ xp web com.amazon.aws.lambda.Ws Greet
@xp.web.srv.Standalone(HTTP @ peer.ServerSocket(Resource id #124 -> tcp://127.0.0.1:8080))
Serving prod:Lambda<Greet>[] > web.logging.ToConsole
════════════════════════════════════════════════════════════════════════
> Server started: http://localhost:8080 in 0.057 seconds
Sat, 18 Nov 2023 12:19:32 +0100 - PID 18668; press Ctrl+C to exit

# ...
```

By adding `-m develop`, these can be run in the development webserver.

Setup and deployment
--------------------
Expand Down
86 changes: 86 additions & 0 deletions src/main/php/xp/lambda/Web.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php namespace xp\lambda;

use com\amazon\aws\lambda\{Context, Environment, RequestContext};
use lang\{XPClass, IllegalArgumentException};
use util\UUID;
use util\cmd\Console;
use web\{Application, Filters};

/** Runs lambda HTTP APIs via `xp web com.amazon.aws.lambda.Ws [class]` */
class Web extends Application {
const TRACE= 'Root=1-5bef4de7-ad49b0e87f6ef6c87fc2e700;Parent=9a9197af755a6419;Sampled=1';
const REGION= 'test-local-1';

private $app;

/**
* Creates a new instance
*
* @param web.Environment $environment
* @throws lang.IllegalArgumentException
*/
public function __construct($environment) {
if (empty($arguments= $environment->arguments())) {
throw new IllegalArgumentException('Need an argument');
}

$this->app= XPClass::forName($arguments[0]);
parent::__construct($environment);
}

/** @return web.Routing */
public function routes() {

// Runtime context
$function= strtolower($this->app->getSimpleName());
$region= $this->environment->variable('AWS_REGION') ?? self::REGION;
$functionArn= "arn:aws:lambda:{$region}:123456789012:function:{$function}";
$headers= [
'Lambda-Runtime-Aws-Request-Id' => [UUID::randomUUID()->hashCode()],
'Lambda-Runtime-Invoked-Function-Arn' => [$functionArn],
'Lambda-Runtime-Trace-Id' => [self::TRACE],
'Lambda-Runtime-Deadline-Ms' => [(time() + 900) * 1000],
];
$context= new Context($headers, $_ENV + [
'AWS_LAMBDA_FUNCTION_NAME' => $function,
'AWS_REGION' => $region,
'AWS_LOCAL' => true,
]);

// See https://github.com/awsdocs/aws-lambda-developer-guide/blob/main/sample-apps/nodejs-apig/event-v2.json
$lambda= function($req, $res, $inv) use($function, $context) {
$via= new RequestContext([
'accountId' => '123456789012',
'apiId' => 'x17bf9mIws',
'domainName' => 'x17bf9mIws.execute-api.test-local-1.amazonaws.com',
'domainPrefix' => 'x17bf9mIws',
'requestId' => 'JKJaXmPLvHcESHA=',
'routeKey' => "ANY /{$function}-function-1G3XMPLZXVXYI",
'stage' => '$default',
'timeEpoch' => time() * 1000,
'http' => [
'method' => $req->method(),
'path' => $req->uri()->path(),
'protocol' => 'HTTP/1.1',
'sourceIp' => $req->header('Remote-Addr'),
'userAgent' => $req->header('User-Agent'),
]
]);

// Add response headers replicating the inconsistent casing AWS uses
$res->header('x-amzn-RequestId', $context->awsRequestId);
$res->header('X-Amzn-Trace-Id', $context->traceId);
return $inv->proceed($req->pass('context', $context)->pass('request', $via), $res);
};

return new Filters([$lambda], $this->app
->newInstance(new Environment($this->environment->webroot(), Console::$out))
->routes($this->enviroment)
);
}

/** @return string */
public function toString() {
return nameof($this).'<'.$this->app->getName().'>';
}
}