Skip to content

Commit a21d19e

Browse files
committed
refactor ro use new scala-aws-lambda-runtime 0.9.5
1 parent da19cfd commit a21d19e

File tree

7 files changed

+130
-81
lines changed

7 files changed

+130
-81
lines changed

ApiGatewayRequestHandler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ package org.encalmo.lambda
33
import scala.util.matching.Regex
44

55
/** Abstract base class of an http request handler. */
6-
trait ApiGatewayRequestHandler[OtherContext] {
6+
trait ApiGatewayRequestHandler[ApplicationContext] {
77

88
type Resource = (String, Regex)
99

1010
def handleRequest(
1111
request: ApiGatewayRequest
12-
)(using LambdaContext, OtherContext): Option[ApiGatewayResponse]
12+
)(using LambdaContext, ApplicationContext): Option[ApiGatewayResponse]
1313

1414
extension (r: Resource)
1515
inline def httpMethod: String = r._1

GenericEvent.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.encalmo.lambda
2+
3+
import upickle.default.ReadWriter
4+
import org.encalmo.lambda.OptionPickler.*
5+
6+
final case class GenericEvent(
7+
funtionName: String,
8+
funtionInput: ujson.Value
9+
) derives ReadWriter

GenericEventHandler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.encalmo.lambda
33
import ujson.Value
44

55
/** Abstract base class of an generic event handler. */
6-
trait GenericEventHandler[OtherContext] {
6+
trait GenericEventHandler[ApplicationContext] {
77

88
/** Optional function name for handling events having `function` property. If not defined it will default to the
99
* simple class name.
@@ -12,5 +12,5 @@ trait GenericEventHandler[OtherContext] {
1212

1313
def handleEvent(
1414
event: Value
15-
)(using LambdaContext, OtherContext): Option[String]
15+
)(using LambdaContext, ApplicationContext): Option[String]
1616
}

MultipleHandlersSupport.scala

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ final case class UnsupportedRequestError(message: String) extends Exception(mess
1111

1212
final case class UnsupportedEventError(message: String) extends Exception(message)
1313

14-
trait MultipleHandlersSupport[OtherContext] extends EventHandler, EventHandlerTag {
15-
16-
given otherContext: OtherContext
14+
trait MultipleHandlersSupport extends EventHandler, EventHandlerTag {
1715

1816
private val functionNameRegex = "\"(?:function|functionName|handler)\"(?:\\s*):(?:\\s*)\"(.+?)\"".r
1917
private val requestPathRegex = "\"path\"(?:\\s*):(?:\\s*)\"(.+?)\"".r
@@ -26,13 +24,13 @@ trait MultipleHandlersSupport[OtherContext] extends EventHandler, EventHandlerTa
2624
.flatMap(m => Option(m.group(1)))
2725
} catch { case NonFatal(_) => None }
2826

29-
def apiGatewayRequestHandlers: Iterable[ApiGatewayRequestHandler[OtherContext]]
27+
def apiGatewayRequestHandlers: Iterable[ApiGatewayRequestHandler[ApplicationContext]]
3028

31-
def sqsEventHandlers: Iterable[SqsEventHandler[OtherContext]]
29+
def sqsEventHandlers: Iterable[SqsEventHandler[ApplicationContext]]
3230

33-
def genericEventHandlers: Iterable[GenericEventHandler[OtherContext]]
31+
def genericEventHandlers: Iterable[GenericEventHandler[ApplicationContext]]
3432

35-
lazy val sqsEventHandlersMap: Map[String, SqsEventHandler[OtherContext]] =
33+
lazy val sqsEventHandlersMap: Map[String, SqsEventHandler[ApplicationContext]] =
3634
sqsEventHandlers
3735
.map(handler =>
3836
(
@@ -47,7 +45,7 @@ trait MultipleHandlersSupport[OtherContext] extends EventHandler, EventHandlerTa
4745
)
4846
.toMap
4947

50-
lazy val genericEventHandlersMap: Map[String, GenericEventHandler[OtherContext]] = genericEventHandlers
48+
lazy val genericEventHandlersMap: Map[String, GenericEventHandler[ApplicationContext]] = genericEventHandlers
5149
.map(handler =>
5250
(
5351
handler.functionName
@@ -61,9 +59,7 @@ trait MultipleHandlersSupport[OtherContext] extends EventHandler, EventHandlerTa
6159
)
6260
.toMap
6361

64-
final override inline def handleRequest(
65-
input: String
66-
)(using lambdaContext: LambdaContext): String =
62+
final override def handleRequest(input: String)(using LambdaContext, ApplicationContext): String =
6763
parseInput(input).match {
6864
case request: ApiGatewayRequest =>
6965
try {
@@ -87,18 +83,23 @@ trait MultipleHandlersSupport[OtherContext] extends EventHandler, EventHandlerTa
8783
sqsEventHandlersMap
8884
.get(functionName)
8985
.flatMap { handler =>
90-
lambdaContext.debug(
91-
s"[EVENT][${index + 1}] Invoking $functionName with payload ${record.writeAsString}"
92-
)
93-
val r = handler.handleRecord(getFunctionInput(record))
94-
lambdaContext.debug(s"[EVENT][${index + 1}] Handler returned result ${r.getOrElse("none")}")
95-
r
86+
handler.handleRecord(getFunctionInput(record))
9687
}
9788
.getOrElse(
9889
throw UnsupportedEventError(write(event))
9990
)
10091
case None =>
101-
throw UnsupportedEventError(write(event))
92+
if (sqsEventHandlersMap.size == 1)
93+
then
94+
sqsEventHandlersMap.head._2
95+
.handleRecord(record)
96+
.getOrElse(
97+
throw UnsupportedEventError(write(event))
98+
)
99+
else
100+
throw UnsupportedEventError(
101+
"Ambiguous SQS event cannot be processed because 'handler' parameter is missing. Add \"handler\":\"{sqsHandlerName}\" field to your record body."
102+
)
102103
}
103104
} catch handleSqsEventHandlerException(input)
104105
}
@@ -115,7 +116,17 @@ trait MultipleHandlersSupport[OtherContext] extends EventHandler, EventHandlerTa
115116
throw UnsupportedEventError(write(event))
116117
)
117118
case None =>
118-
throw UnsupportedEventError(write(event))
119+
if (genericEventHandlersMap.size == 1)
120+
then
121+
genericEventHandlersMap.head._2
122+
.handleEvent(event)
123+
.getOrElse(
124+
throw UnsupportedEventError(write(event))
125+
)
126+
else
127+
throw UnsupportedEventError(
128+
"Ambiguous generic event cannot be processed because 'handler' parameter is missing. Add \"handler\":\"{genericHandlerName}\" field to your event body."
129+
)
119130
}
120131

121132
} catch handleGenericEventHandlerException(input)
@@ -203,34 +214,33 @@ trait MultipleHandlersSupport[OtherContext] extends EventHandler, EventHandlerTa
203214

204215
private inline def maybeFunctionName(json: ujson.Value): Option[String] =
205216
json
206-
.get("functionName")
207-
.flatMap(_.strOpt)
208-
.orElse(
209-
json
210-
.get("function")
211-
.flatMap(_.strOpt)
212-
)
213-
.orElse(
214-
json
215-
.get("handler")
216-
.flatMap(_.strOpt)
217-
)
217+
.maybeString("functionName")
218+
.orElse(json.maybeString("handlerName"))
219+
.orElse(json.maybeString("handler"))
220+
.orElse(json.maybeString("function"))
218221

219222
private inline def getFunctionInput(event: ujson.Value): ujson.Value =
220-
event.get("functionInputParts") match {
221-
case Some(ujson.Arr(values)) =>
222-
values.foldLeft(ujson.Obj()) { (a, v) =>
223-
v match {
224-
case Obj(newFields) => Obj.from(a.value ++ newFields)
225-
case other =>
226-
throw new Exception(
227-
s"Expected functionInputParts array to contain only objects, but got ${other.getClass().getSimpleName()} $other"
228-
)
223+
event
224+
.get("functionInputParts")
225+
.orElse(event.get("handlerInputParts"))
226+
.match {
227+
case Some(ujson.Arr(values)) =>
228+
values.foldLeft(ujson.Obj()) { (a, v) =>
229+
v match {
230+
case Obj(newFields) => Obj.from(a.value ++ newFields)
231+
case other =>
232+
throw new Exception(
233+
s"Expected functionInputParts array to contain only objects, but got ${other.getClass().getSimpleName()} $other"
234+
)
235+
}
229236
}
230-
}
231-
case Some(value) => value
232-
case None => event.get("functionInput").getOrElse(event)
233-
}
237+
case Some(value) => value
238+
case None =>
239+
event
240+
.get("functionInput")
241+
.orElse(event.get("handlerInput"))
242+
.getOrElse(event)
243+
}
234244

235245
private inline def getFunctionInput(record: SqsEvent.Record): SqsEvent.Record =
236246
record.copy(body =

MultipleHandlersSupport.test.scala

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import ujson.Value
66
import upickle.default.*
77
import java.time.Instant
88

9+
class Foo
10+
911
class MultipleHandlersSupportSpec extends munit.FunSuite {
1012

1113
override def munitFixtures = List(lambdaService)
1214

1315
val lambdaService = new LambdaServiceFixture()
1416

15-
class Foo
16-
1717
test("Execute generic event handler") {
1818

1919
assertEquals(
@@ -31,6 +31,16 @@ class MultipleHandlersSupportSpec extends munit.FunSuite {
3131
"bar"
3232
)
3333

34+
assertEquals(
35+
lambdaRuntime.test("""{"handlerName":"TestMe","foo":"bar"}"""),
36+
"bar"
37+
)
38+
39+
assertEquals(
40+
lambdaRuntime.test("""{"foo":"bar"}"""),
41+
"bar"
42+
)
43+
3444
assertEquals(
3545
lambdaRuntime.test("""{"bar":"foo"}"""),
3646
"""{"success":false,"timestamp":"""
@@ -45,15 +55,15 @@ class MultipleHandlersSupportSpec extends munit.FunSuite {
4555
"bar"
4656
)
4757
assertEquals(
48-
lambdaRuntime.test("""{"function":"TestMe", "functionInput": {"foo":"bar"}}"""),
58+
lambdaRuntime.test("""{"functionName":"TestMe", "functionInput": {"foo":"bar"}}"""),
4959
"bar"
5060
)
5161
assertEquals(
52-
lambdaRuntime.test("""{"function":"TestMe", "functionInputParts": {"foo":"bar"}}"""),
62+
lambdaRuntime.test("""{"handler":"TestMe", "functionInputParts": {"foo":"bar"}}"""),
5363
"bar"
5464
)
5565
assertEquals(
56-
lambdaRuntime.test("""{"function":"TestMe", "functionInputParts": [{"foo":"bar"}]}"""),
66+
lambdaRuntime.test("""{"handlerName":"TestMe", "handlerInputParts": [{"foo":"bar"}]}"""),
5767
"bar"
5868
)
5969
assertEquals(
@@ -148,41 +158,61 @@ class MultipleHandlersSupportSpec extends munit.FunSuite {
148158
override def afterAll(): Unit =
149159
lambdaService.close()
150160

151-
def lambdaRuntime = new LambdaRuntime with MultipleHandlersSupport[Foo] {
161+
def lambdaRuntime =
162+
new LambdaRuntime with MultipleHandlersSupport {
152163

153-
override given otherContext: Foo = new Foo
164+
override type ApplicationContext = Foo
154165

155-
override def apiGatewayRequestHandlers: Iterable[ApiGatewayRequestHandler[Foo]] = Seq.empty
166+
override def initialize(using environment: LambdaEnvironment) = {
167+
environment.info(
168+
s"Initializing lambda ${environment.getFunctionName()} ..."
169+
)
170+
new Foo
171+
}
156172

157-
override def sqsEventHandlers: Iterable[SqsEventHandler[Foo]] = Seq(
158-
new SqsEventHandler[Foo] {
173+
override def apiGatewayRequestHandlers = Seq(new TestApiGatewayRequestHandler)
159174

160-
override def functionName: Option[String] = Some("TestMe")
175+
override def sqsEventHandlers = Seq(new TestSqsEventHandler)
161176

162-
override def handleRecord(record: SqsEvent.Record)(using
163-
LambdaContext,
164-
Foo
165-
): Option[String] =
166-
record.maybeParseBodyAsJson.flatMap(_.maybeString("foo"))
177+
override def genericEventHandlers = Seq(new TestGenericEventHandler)
178+
}
167179

168-
}
169-
)
180+
}
170181

171-
override def genericEventHandlers: Iterable[GenericEventHandler[Foo]] =
172-
Seq(new GenericEventHandler[Foo] {
182+
class TestGenericEventHandler extends GenericEventHandler[Foo] {
173183

174-
override def functionName: Option[String] = Some("TestMe")
184+
override def functionName: Option[String] = Some("TestMe")
175185

176-
override def handleEvent(event: Value)(using
177-
LambdaContext,
178-
Foo
179-
): Option[String] = event.maybeString("foo")
186+
override def handleEvent(event: Value)(using
187+
LambdaContext,
188+
Foo
189+
): Option[String] = event.maybeString("foo")
180190

181-
})
191+
}
182192

183-
lazy val config = configure { (environment: LambdaEnvironment) =>
184-
println(s"Initializing lambda ${environment.getFunctionName()} ...")
185-
}
186-
}
193+
class TestSqsEventHandler extends SqsEventHandler[Foo] {
194+
195+
override def functionName: Option[String] = Some("TestMe")
196+
197+
override def handleRecord(record: SqsEvent.Record)(using
198+
LambdaContext,
199+
Foo
200+
): Option[String] =
201+
record.maybeParseBodyAsJson.flatMap(_.maybeString("foo"))
202+
}
203+
204+
class TestApiGatewayRequestHandler extends ApiGatewayRequestHandler[Foo] {
205+
206+
override def handleRequest(request: ApiGatewayRequest)(using LambdaContext, Foo): Option[ApiGatewayResponse] =
207+
request.body.maybeReadAsJson
208+
.flatMap(_.maybeString("foo"))
209+
.map(foo =>
210+
ApiGatewayResponse(
211+
body = foo,
212+
statusCode = 200,
213+
headers = Map.empty,
214+
isBase64Encoded = false
215+
)
216+
)
187217

188218
}

SqsEventHandler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.encalmo.lambda
22

33
/** Abstract base class of an SQS event handler. */
4-
trait SqsEventHandler[OtherContext] {
4+
trait SqsEventHandler[ApplicationContext] {
55

66
/** Optional function name for handling events having `function` property. If not defined it will default to the
77
* simple class name.
@@ -10,5 +10,5 @@ trait SqsEventHandler[OtherContext] {
1010

1111
def handleRecord(
1212
record: SqsEvent.Record
13-
)(using LambdaContext, OtherContext): Option[String]
13+
)(using LambdaContext, ApplicationContext): Option[String]
1414
}

project.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//> using scala 3.3.5
22
//> using exclude scripts
3-
//> using dep org.encalmo::scala-aws-lambda-runtime:0.9.0
3+
//> using dep org.encalmo::scala-aws-lambda-runtime:0.9.5
44
//> using dep org.encalmo::scala-aws-lambda-utils:0.9.2
55
//> using test.dep org.encalmo::scala-aws-lambda-testkit:0.9.0
66
//> using test.dep org.scalameta::munit:1.1.0

0 commit comments

Comments
 (0)