Skip to content

Commit

Permalink
Merge pull request #11 from xp-forge/feature/xp-web
Browse files Browse the repository at this point in the history
Run lambda HTTP APIs via `xp web lambda [class]`
  • Loading branch information
thekid committed Nov 20, 2023
2 parents a38001e + 68c21a4 commit 39cbe7b
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 1 deletion.
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().'>';
}
}

0 comments on commit 39cbe7b

Please sign in to comment.