Skip to content

Commit ea4f2af

Browse files
(JS) Expose handler function type to allow manual exporting of Lambda handler
Related to #246
1 parent 8edd293 commit ea4f2af

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

lambda/js/src/main/scala/feral/lambda/IOLambdaPlatform.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,23 @@ import scala.scalajs.js.|
2626
private[lambda] trait IOLambdaPlatform[Event, Result] {
2727
this: IOLambda[Event, Result] =>
2828

29+
/**
30+
* Lambda handler. Implement this type with a val and a call to `handlerFn` to export your
31+
* handler.
32+
*
33+
* @example
34+
* {{{
35+
* val handler: HandlerFn = handlerFn
36+
* }}}
37+
*/
38+
final type HandlerFn = js.Function2[js.Any, facade.Context, js.Promise[js.Any | Unit]]
39+
2940
final def main(args: Array[String]): Unit =
3041
js.Dynamic.global.exports.updateDynamic(handlerName)(handlerFn)
3142

3243
protected def handlerName: String = getClass.getSimpleName.init
3344

34-
private lazy val handlerFn
45+
protected lazy val handlerFn
3546
: js.Function2[js.Any, facade.Context, js.Promise[js.Any | Unit]] = {
3647
(event: js.Any, context: facade.Context) =>
3748
(for {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package feral.lambda
2+
3+
import cats.effect.IO
4+
import cats.effect.Ref
5+
import cats.effect.kernel.Resource
6+
import cats.syntax.all._
7+
import io.circe.scalajs._
8+
import munit.CatsEffectSuite
9+
10+
import scala.scalajs.js
11+
import scala.scalajs.js.|
12+
13+
class ExportedLambdaSuite extends CatsEffectSuite {
14+
test("exported lambda") {
15+
val context = DummyContext.asInstanceOf[facade.Context]
16+
17+
for {
18+
allocationCounter <- IO.ref(0)
19+
invokeCounter <- IO.ref(0)
20+
lambda = new CountingIOLambda(allocationCounter, invokeCounter)
21+
22+
_ <- ('0' to 'z')
23+
.map(_.toString)
24+
.toList
25+
.traverse(x =>
26+
IO.fromPromise(IO(lambda.impl(x, context)))
27+
.assertEquals(x.asJsAny.asInstanceOf[js.Any | Unit]))
28+
29+
_ <- allocationCounter.get.assertEquals(1)
30+
_ <- invokeCounter.get.assertEquals(75)
31+
} yield ()
32+
33+
}
34+
}
35+
36+
class CountingIOLambda(allocationCounter: Ref[IO, Int], invokeCounter: Ref[IO, Int])
37+
extends IOLambda[String, String] {
38+
39+
override def handler: Resource[IO, LambdaEnv[IO, String] => IO[Option[String]]] =
40+
Resource
41+
.eval(allocationCounter.getAndUpdate(_ + 1))
42+
.as(_.event.map(Some(_)) <* invokeCounter.getAndUpdate(_ + 1))
43+
44+
// This would be exported with `@JSExportTopLevel("handler")`
45+
def impl: HandlerFn = handlerFn
46+
}
47+
48+
object DummyContext extends js.Object {
49+
def callbackWaitsForEmptyEventLoop: Boolean = true
50+
def functionName: String = ""
51+
def functionVersion: String = ""
52+
def invokedFunctionArn: String = ""
53+
def memoryLimitInMB: String = "512"
54+
def awsRequestId: String = ""
55+
def logGroupName: String = ""
56+
def logStreamName: String = ""
57+
def identity: js.UndefOr[CognitoIdentity] = js.undefined
58+
def clientContext: js.UndefOr[ClientContext] = js.undefined
59+
def getRemainingTimeInMillis(): Double = 0
60+
}

0 commit comments

Comments
 (0)