Skip to content

Commit

Permalink
(JS) Expose handler function type to allow manual exporting of Lambda…
Browse files Browse the repository at this point in the history
… handler

Related to typelevel#246
  • Loading branch information
hugo-vrijswijk committed Jul 23, 2022
1 parent 8edd293 commit e175631
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 1 deletion.
13 changes: 12 additions & 1 deletion lambda/js/src/main/scala/feral/lambda/IOLambdaPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,23 @@ import scala.scalajs.js.|
private[lambda] trait IOLambdaPlatform[Event, Result] {
this: IOLambda[Event, Result] =>

/**
* Lambda handler. Implement this type with a val and a call to `handlerFn` to export your
* handler.
*
* @example
* {{{
* val handler: HandlerFn = handlerFn
* }}}
*/
final type HandlerFn = js.Function2[js.Any, facade.Context, js.Promise[js.Any | Unit]]

final def main(args: Array[String]): Unit =
js.Dynamic.global.exports.updateDynamic(handlerName)(handlerFn)

protected def handlerName: String = getClass.getSimpleName.init

private lazy val handlerFn
protected lazy val handlerFn
: js.Function2[js.Any, facade.Context, js.Promise[js.Any | Unit]] = {
(event: js.Any, context: facade.Context) =>
(for {
Expand Down
76 changes: 76 additions & 0 deletions lambda/js/src/test/scala/feral/lambda/ExportedLambdaSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2021 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package feral.lambda

import cats.effect.IO
import cats.effect.Ref
import cats.effect.kernel.Resource
import cats.syntax.all._
import io.circe.scalajs._
import munit.CatsEffectSuite

import scala.scalajs.js
import scala.scalajs.js.|

class ExportedLambdaSuite extends CatsEffectSuite {
test("exported lambda") {
val context = DummyContext.asInstanceOf[facade.Context]

for {
allocationCounter <- IO.ref(0)
invokeCounter <- IO.ref(0)
lambda = new CountingIOLambda(allocationCounter, invokeCounter)

_ <- ('0' to 'z')
.map(_.toString)
.toList
.traverse(x =>
IO.fromPromise(IO(lambda.impl(x, context)))
.assertEquals(x.asJsAny.asInstanceOf[js.Any | Unit]))

_ <- allocationCounter.get.assertEquals(1)
_ <- invokeCounter.get.assertEquals(75)
} yield ()

}
}

class CountingIOLambda(allocationCounter: Ref[IO, Int], invokeCounter: Ref[IO, Int])
extends IOLambda[String, String] {

override def handler: Resource[IO, LambdaEnv[IO, String] => IO[Option[String]]] =
Resource
.eval(allocationCounter.getAndUpdate(_ + 1))
.as(_.event.map(Some(_)) <* invokeCounter.getAndUpdate(_ + 1))

// This would be exported with `@JSExportTopLevel("handler")`
def impl: HandlerFn = handlerFn
}

object DummyContext extends js.Object {
def callbackWaitsForEmptyEventLoop: Boolean = true
def functionName: String = ""
def functionVersion: String = ""
def invokedFunctionArn: String = ""
def memoryLimitInMB: String = "512"
def awsRequestId: String = ""
def logGroupName: String = ""
def logStreamName: String = ""
def identity: js.UndefOr[CognitoIdentity] = js.undefined
def clientContext: js.UndefOr[ClientContext] = js.undefined
def getRemainingTimeInMillis(): Double = 0
}

0 comments on commit e175631

Please sign in to comment.