-
-
Notifications
You must be signed in to change notification settings - Fork 49
Add ApplicationLoadBalancerRequestEvent #590
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 15 commits
56aec7f
25b7b8b
ba95f35
85aee56
fd5316c
2cbbeed
3e77117
e0d2bff
d62086e
babc30f
b23c456
f2521ce
33a6b4d
ad2a0f9
71b6567
789d714
fbabd37
4f219a8
829f70a
0c09c43
042cd17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,116 @@ | ||||||
| /* | ||||||
| * 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.events | ||||||
|
|
||||||
| import io.circe.Decoder | ||||||
| import org.typelevel.ci.CIString | ||||||
|
|
||||||
| final case class Elb(targetGroupArn: String) | ||||||
|
|
||||||
| object Elb { | ||||||
| implicit val decoder: Decoder[Elb] = Decoder.forProduct1("targetGroupArn")(Elb.apply) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| sealed abstract class ApplicationLoadBalancerRequestContext { | ||||||
| def elb: Elb | ||||||
| } | ||||||
|
|
||||||
| object ApplicationLoadBalancerRequestContext { | ||||||
|
|
||||||
| def apply(elb: Elb): ApplicationLoadBalancerRequestContext = | ||||||
| Impl(elb) | ||||||
|
|
||||||
| implicit val decoder: Decoder[ApplicationLoadBalancerRequestContext] = | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| Decoder.forProduct1("elb")(ApplicationLoadBalancerRequestContext.apply) | ||||||
|
|
||||||
| private final case class Impl(elb: Elb) extends ApplicationLoadBalancerRequestContext | ||||||
| } | ||||||
|
|
||||||
| sealed abstract class ApplicationLoadBalancerRequestEvent { | ||||||
| def requestContext: ApplicationLoadBalancerRequestContext | ||||||
| def httpMethod: String | ||||||
| def path: String | ||||||
| def queryStringParameters: Option[Map[String, String]] | ||||||
| def headers: Option[Map[CIString, String]] | ||||||
| def multiValueQueryStringParameters: Option[Map[String, List[String]]] | ||||||
| def multiValueHeaders: Option[Map[CIString, List[String]]] | ||||||
| def body: Option[String] | ||||||
| def isBase64Encoded: Boolean | ||||||
| def decodedBody: Option[Array[Byte]] = | ||||||
| body.map { b => | ||||||
| if (isBase64Encoded) java.util.Base64.getDecoder.decode(b) | ||||||
| else b.getBytes(java.nio.charset.StandardCharsets.UTF_8) | ||||||
| } | ||||||
ngwin-ab marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| } | ||||||
|
|
||||||
| object ApplicationLoadBalancerRequestEvent { | ||||||
| private implicit val ciStringKeyDecoder: io.circe.KeyDecoder[CIString] = | ||||||
| io.circe.KeyDecoder.instance(s => Some(CIString(s))) | ||||||
ngwin-ab marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| private implicit val mapStringDecoder: Decoder[Map[CIString, String]] = | ||||||
| Decoder.decodeMap[CIString, String] | ||||||
| private implicit val mapListDecoder: Decoder[Map[CIString, List[String]]] = | ||||||
| Decoder.decodeMap[CIString, List[String]] | ||||||
|
||||||
|
|
||||||
| def apply( | ||||||
| requestContext: ApplicationLoadBalancerRequestContext, | ||||||
| httpMethod: String, | ||||||
| path: String, | ||||||
| queryStringParameters: Option[Map[String, String]], | ||||||
| headers: Option[Map[CIString, String]], | ||||||
| multiValueQueryStringParameters: Option[Map[String, List[String]]], | ||||||
| multiValueHeaders: Option[Map[CIString, List[String]]], | ||||||
| body: Option[String], | ||||||
| isBase64Encoded: Boolean | ||||||
| ): ApplicationLoadBalancerRequestEvent = | ||||||
| Impl( | ||||||
| requestContext, | ||||||
| httpMethod, | ||||||
| path, | ||||||
| queryStringParameters, | ||||||
| headers, | ||||||
| multiValueQueryStringParameters, | ||||||
| multiValueHeaders, | ||||||
| body, | ||||||
| isBase64Encoded | ||||||
| ) | ||||||
|
|
||||||
| implicit val decoder: Decoder[ApplicationLoadBalancerRequestEvent] = | ||||||
| Decoder.forProduct9( | ||||||
| "requestContext", | ||||||
| "httpMethod", | ||||||
| "path", | ||||||
| "queryStringParameters", | ||||||
| "headers", | ||||||
| "multiValueQueryStringParameters", | ||||||
| "multiValueHeaders", | ||||||
| "body", | ||||||
| "isBase64Encoded" | ||||||
| )(ApplicationLoadBalancerRequestEvent.apply) | ||||||
|
|
||||||
| private final case class Impl( | ||||||
| requestContext: ApplicationLoadBalancerRequestContext, | ||||||
| httpMethod: String, | ||||||
| path: String, | ||||||
| queryStringParameters: Option[Map[String, String]], | ||||||
| headers: Option[Map[CIString, String]], | ||||||
| multiValueQueryStringParameters: Option[Map[String, List[String]]], | ||||||
| multiValueHeaders: Option[Map[CIString, List[String]]], | ||||||
| body: Option[String], | ||||||
| isBase64Encoded: Boolean | ||||||
| ) extends ApplicationLoadBalancerRequestEvent | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| /* | ||
| * 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.events | ||
|
|
||
| import io.circe.Json | ||
| import io.circe.literal._ | ||
| import munit.FunSuite | ||
|
|
||
| object ApplicationLoadBalancerRequestEventSuite { | ||
| def allFieldsEvent = json""" | ||
| { | ||
| "requestContext": { | ||
| "elb": { | ||
| "targetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09" | ||
| } | ||
| }, | ||
| "httpMethod": "GET", | ||
| "path": "/lambda", | ||
| "queryStringParameters": { | ||
| "query": "1234ABCD" | ||
| }, | ||
| "headers": { | ||
| "accept": "text/html,application/xhtml+xml", | ||
| "accept-language": "en-US,en;q=0.8", | ||
| "content-type": "text/plain", | ||
| "cookie": "cookies", | ||
| "host": "lambda-846800462-us-west-2.elb.amazonaws.com", | ||
| "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)", | ||
| "x-amzn-trace-id": "Root=1-58337364-23a8c76965a2ef7629b2b5c2", | ||
| "x-forwarded-for": "72.21.198.64", | ||
| "x-forwarded-port": "443", | ||
| "x-forwarded-proto": "https" | ||
| }, | ||
| "multiValueQueryStringParameters": null, | ||
| "multiValueHeaders": null, | ||
| "body": null, | ||
| "isBase64Encoded": false | ||
| } | ||
| """ | ||
|
|
||
| def withBodyEvent = json""" | ||
| { | ||
| "requestContext": { | ||
| "elb": { | ||
| "targetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09" | ||
| } | ||
| }, | ||
| "httpMethod": "POST", | ||
| "path": "/submit", | ||
| "queryStringParameters": null, | ||
| "headers": null, | ||
| "multiValueQueryStringParameters": null, | ||
| "multiValueHeaders": null, | ||
| "body": "hello world", | ||
| "isBase64Encoded": true | ||
ngwin-ab marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
ngwin-ab marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| """ | ||
|
|
||
| def missingOptionalsEvent = json""" | ||
| { | ||
| "requestContext": { | ||
| "elb": { | ||
| "targetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09" | ||
| } | ||
| }, | ||
| "httpMethod": "GET", | ||
| "path": "/only-required", | ||
| "isBase64Encoded": false | ||
| } | ||
| """ | ||
| } | ||
|
|
||
| class ApplicationLoadBalancerRequestEventSuite extends FunSuite { | ||
| import ApplicationLoadBalancerRequestEventSuite.* | ||
|
|
||
| test("decode with all fields populated") { | ||
| val decoded = allFieldsEvent.as[ApplicationLoadBalancerRequestEvent].toTry.get | ||
| val expected = ApplicationLoadBalancerRequestEvent( | ||
| requestContext = ApplicationLoadBalancerRequestContext( | ||
| elb = Elb( | ||
| "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09") | ||
| ), | ||
| httpMethod = "GET", | ||
| path = "/lambda", | ||
| queryStringParameters = Some(Map("query" -> "1234ABCD")), | ||
| headers = Some( | ||
| Map( | ||
| org.typelevel.ci.CIString("accept") -> "text/html,application/xhtml+xml", | ||
| org.typelevel.ci.CIString("accept-language") -> "en-US,en;q=0.8", | ||
| org.typelevel.ci.CIString("content-type") -> "text/plain", | ||
| org.typelevel.ci.CIString("cookie") -> "cookies", | ||
| org.typelevel.ci.CIString("host") -> "lambda-846800462-us-west-2.elb.amazonaws.com", | ||
| org | ||
| .typelevel | ||
| .ci | ||
| .CIString("user-agent") -> "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)", | ||
| org | ||
| .typelevel | ||
| .ci | ||
| .CIString("x-amzn-trace-id") -> "Root=1-58337364-23a8c76965a2ef7629b2b5c2", | ||
| org.typelevel.ci.CIString("x-forwarded-for") -> "72.21.198.64", | ||
| org.typelevel.ci.CIString("x-forwarded-port") -> "443", | ||
| org.typelevel.ci.CIString("x-forwarded-proto") -> "https" | ||
| ) | ||
| ), | ||
| multiValueQueryStringParameters = None, | ||
| multiValueHeaders = None, | ||
| body = None, | ||
| isBase64Encoded = false | ||
| ) | ||
| assertEquals(decoded, expected) | ||
| } | ||
|
|
||
| test("decode with only body present") { | ||
| val decoded = withBodyEvent.as[ApplicationLoadBalancerRequestEvent].toTry.get | ||
| val expected = ApplicationLoadBalancerRequestEvent( | ||
| requestContext = ApplicationLoadBalancerRequestContext( | ||
| elb = Elb( | ||
| "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09") | ||
| ), | ||
| httpMethod = "POST", | ||
| path = "/submit", | ||
| queryStringParameters = None, | ||
| headers = None, | ||
| multiValueQueryStringParameters = None, | ||
| multiValueHeaders = None, | ||
| body = Some("hello world"), | ||
| isBase64Encoded = true | ||
| ) | ||
| assertEquals(decoded, expected) | ||
| } | ||
|
|
||
| test("decode with only required fields") { | ||
| val decoded = missingOptionalsEvent.as[ApplicationLoadBalancerRequestEvent].toTry.get | ||
| val expected = ApplicationLoadBalancerRequestEvent( | ||
| requestContext = ApplicationLoadBalancerRequestContext( | ||
| elb = Elb( | ||
| "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09") | ||
| ), | ||
| httpMethod = "GET", | ||
| path = "/only-required", | ||
| queryStringParameters = None, | ||
| headers = None, | ||
| multiValueQueryStringParameters = None, | ||
| multiValueHeaders = None, | ||
| body = None, | ||
| isBase64Encoded = false | ||
| ) | ||
| assertEquals(decoded, expected) | ||
| } | ||
|
|
||
| test("decode base64 body correctly") { | ||
| val base64String = | ||
| java.util.Base64.getEncoder.encodeToString("hello world".getBytes("UTF-8")) | ||
| val eventJson = Json.obj( | ||
| "requestContext" -> Json.obj( | ||
| "elb" -> Json.obj( | ||
| "targetGroupArn" -> Json.fromString( | ||
| "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09") | ||
| ) | ||
| ), | ||
| "httpMethod" -> Json.fromString("POST"), | ||
| "path" -> Json.fromString("/submit"), | ||
| "body" -> Json.fromString(base64String), | ||
| "isBase64Encoded" -> Json.fromBoolean(true) | ||
| ) | ||
| val decoded = eventJson.as[ApplicationLoadBalancerRequestEvent].toTry.get | ||
| assert(decoded.decodedBody.exists(_.sameElements("hello world".getBytes("UTF-8")))) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.