Skip to content

Commit 7b24b82

Browse files
committed
add PhysicalResourceId to CloudFormation Custom Resource interface, where appropriate
PhysicalResourceId is "always sent with Update and Delete requests; never sent with Create" (according to AWS docs) and is commonly required to actually perform the work needed to do an update or delete of the resource. This PR encodes that contract and makes life a bit easier for developers implementing update/delete, since they won't have to deal with the optionality of the value in the event encoding.
1 parent 3f96fe6 commit 7b24b82

4 files changed

Lines changed: 36 additions & 19 deletions

File tree

lambda-cloudformation-custom-resource/src/main/scala/feral/lambda/cloudformation/CloudFormationCustomResource.scala

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ import org.http4s.client.dsl.Http4sClientDsl
3030

3131
trait CloudFormationCustomResource[F[_], Input, Output] {
3232
def createResource(input: Input): F[HandlerResponse[Output]]
33-
def updateResource(input: Input): F[HandlerResponse[Output]]
34-
def deleteResource(input: Input): F[HandlerResponse[Output]]
33+
def updateResource(
34+
input: Input,
35+
physicalResourceId: PhysicalResourceId): F[HandlerResponse[Output]]
36+
def deleteResource(
37+
input: Input,
38+
physicalResourceId: PhysicalResourceId): F[HandlerResponse[Output]]
3539
}
3640

3741
object CloudFormationCustomResource {
@@ -47,11 +51,11 @@ object CloudFormationCustomResource {
4751
import http4sClientDsl._
4852

4953
env.event.flatMap { event =>
50-
(event.RequestType match {
51-
case CreateRequest => handler.createResource(event.ResourceProperties)
52-
case UpdateRequest => handler.updateResource(event.ResourceProperties)
53-
case DeleteRequest => handler.deleteResource(event.ResourceProperties)
54-
case OtherRequestType(other) => illegalRequestType(other)
54+
((event.RequestType, event.PhysicalResourceId) match {
55+
case (CreateRequest, None) => handler.createResource(event.ResourceProperties)
56+
case (UpdateRequest, Some(id)) => handler.updateResource(event.ResourceProperties, id)
57+
case (DeleteRequest, Some(id)) => handler.deleteResource(event.ResourceProperties, id)
58+
case (other, _) => illegalRequestType(other.toString)
5559
}).attempt
5660
.map(_.fold(exceptionResponse(event)(_), successResponse(event)(_)))
5761
.flatMap { resp => client.successful(PUT(resp.asJson, event.ResponseURL)) }

lambda-cloudformation-custom-resource/src/main/scala/feral/lambda/cloudformation/package.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ package cloudformation {
7272
case object CreateRequest extends CloudFormationRequestType
7373
case object UpdateRequest extends CloudFormationRequestType
7474
case object DeleteRequest extends CloudFormationRequestType
75-
final case class OtherRequestType(requestType: String) extends CloudFormationRequestType
75+
final case class OtherRequestType(requestType: String) extends CloudFormationRequestType {
76+
override def toString: String = requestType
77+
}
7678

7779
implicit val encoder: Encoder[CloudFormationRequestType] = {
7880
case CreateRequest => "Create".asJson

lambda-cloudformation-custom-resource/src/test/scala/feral/lambda/cloudformation/CloudFormationCustomResourceArbitraries.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ trait CloudFormationCustomResourceArbitraries {
165165
requestId <- arbitrary[RequestId]
166166
resourceType <- arbitrary[ResourceType]
167167
logicalResourceId <- arbitrary[LogicalResourceId]
168-
physicalResourceId <- arbitrary[Option[PhysicalResourceId]]
168+
physicalResourceId <- arbitrary[PhysicalResourceId]
169169
resourceProperties <- arbitrary[A]
170170
oldResourceProperties <- arbitrary[Option[JsonObject]]
171171
} yield CloudFormationCustomResourceRequest(
@@ -175,7 +175,7 @@ trait CloudFormationCustomResourceArbitraries {
175175
requestId,
176176
resourceType,
177177
logicalResourceId,
178-
physicalResourceId,
178+
if (requestType == CreateRequest) None else physicalResourceId.some,
179179
resourceProperties,
180180
oldResourceProperties
181181
)

lambda-cloudformation-custom-resource/src/test/scala/feral/lambda/cloudformation/ResponseSerializationSuite.scala

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class ResponseSerializationSuite
8383
}
8484
)
8585
.deepDropNullValues
86-
case _ =>
86+
case CreateRequest =>
8787
Json.obj(
8888
"Status" -> "SUCCESS".asJson,
8989
"PhysicalResourceId" -> convertInputToFakePhysicalResourceId(
@@ -93,6 +93,15 @@ class ResponseSerializationSuite
9393
"LogicalResourceId" -> event.LogicalResourceId.asJson,
9494
"Data" -> event.RequestType.asJson
9595
)
96+
case UpdateRequest | DeleteRequest =>
97+
Json.obj(
98+
"Status" -> "SUCCESS".asJson,
99+
"PhysicalResourceId" -> event.PhysicalResourceId.get.asJson,
100+
"StackId" -> event.StackId.asJson,
101+
"RequestId" -> event.RequestId.asJson,
102+
"LogicalResourceId" -> event.LogicalResourceId.asJson,
103+
"Data" -> event.RequestType.asJson
104+
)
96105
}
97106

98107
expect(body eqv expectedJson)
@@ -142,14 +151,16 @@ object ResponseSerializationSuite {
142151
convertInputToFakePhysicalResourceId(input),
143152
CreateRequest.some.widen[CloudFormationRequestType]).pure[F]
144153

145-
override def updateResource(input: String): F[HandlerResponse[CloudFormationRequestType]] =
146-
HandlerResponse(
147-
convertInputToFakePhysicalResourceId(input),
148-
UpdateRequest.some.widen[CloudFormationRequestType]).pure[F]
154+
override def updateResource(
155+
input: String,
156+
physicalResourceId: PhysicalResourceId): F[HandlerResponse[CloudFormationRequestType]] =
157+
HandlerResponse(physicalResourceId, UpdateRequest.some.widen[CloudFormationRequestType])
158+
.pure[F]
149159

150-
override def deleteResource(input: String): F[HandlerResponse[CloudFormationRequestType]] =
151-
HandlerResponse(
152-
convertInputToFakePhysicalResourceId(input),
153-
DeleteRequest.some.widen[CloudFormationRequestType]).pure[F]
160+
override def deleteResource(
161+
input: String,
162+
physicalResourceId: PhysicalResourceId): F[HandlerResponse[CloudFormationRequestType]] =
163+
HandlerResponse(physicalResourceId, DeleteRequest.some.widen[CloudFormationRequestType])
164+
.pure[F]
154165
}
155166
}

0 commit comments

Comments
 (0)