1414
1515package software .amazon .lambda .powertools .cloudformation ;
1616
17- import com .amazonaws .services .lambda .runtime .Context ;
18- import com .amazonaws .services .lambda .runtime .events .CloudFormationCustomResourceEvent ;
19- import com .fasterxml .jackson .databind .JsonNode ;
20- import com .fasterxml .jackson .databind .ObjectMapper ;
21- import com .fasterxml .jackson .databind .PropertyNamingStrategies ;
22- import com .fasterxml .jackson .databind .node .ObjectNode ;
2317import java .io .IOException ;
2418import java .net .URI ;
2519import java .util .Collections ;
2620import java .util .HashMap ;
2721import java .util .List ;
2822import java .util .Map ;
2923import java .util .Objects ;
24+
3025import org .slf4j .Logger ;
3126import org .slf4j .LoggerFactory ;
27+
28+ import com .amazonaws .services .lambda .runtime .Context ;
29+ import com .amazonaws .services .lambda .runtime .events .CloudFormationCustomResourceEvent ;
30+ import com .fasterxml .jackson .databind .JsonNode ;
31+ import com .fasterxml .jackson .databind .ObjectMapper ;
32+ import com .fasterxml .jackson .databind .PropertyNamingStrategies ;
33+ import com .fasterxml .jackson .databind .node .ObjectNode ;
34+
3235import software .amazon .awssdk .http .Header ;
3336import software .amazon .awssdk .http .HttpExecuteRequest ;
3437import software .amazon .awssdk .http .HttpExecuteResponse ;
3942import software .amazon .awssdk .utils .StringUtils ;
4043
4144/**
42- * Client for sending responses to AWS CloudFormation custom resources by way of a response URL, which is an Amazon S3
45+ * Client for sending responses to AWS CloudFormation custom resources by way of
46+ * a response URL, which is an Amazon S3
4347 * pre-signed URL.
4448 * <p>
45- * See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html
49+ * See
50+ * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html
4651 * <p>
47- * This class is thread-safe provided the SdkHttpClient instance used is also thread-safe.
52+ * This class is thread-safe provided the SdkHttpClient instance used is also
53+ * thread-safe.
4854 */
49- class CloudFormationResponse {
55+ public class CloudFormationResponse {
5056
5157 private static final Logger LOG = LoggerFactory .getLogger (CloudFormationResponse .class );
5258 private final SdkHttpClient client ;
5359
5460 /**
55- * Creates a new CloudFormationResponse that uses the provided HTTP client and default JSON serialization format.
61+ * Creates a new CloudFormationResponse that uses the provided HTTP client and
62+ * default JSON serialization format.
5663 *
5764 * @param client HTTP client to use for sending requests; cannot be null
5865 */
@@ -70,36 +77,46 @@ SdkHttpClient getClient() {
7077 }
7178
7279 /**
73- * Forwards a response containing a custom payload to the target resource specified by the event. The payload is
80+ * Forwards a response containing a custom payload to the target resource
81+ * specified by the event. The payload is
7482 * formed from the event and context data. Status is assumed to be SUCCESS.
7583 *
7684 * @param event custom CF resource event. Cannot be null.
77- * @param context used to specify when the function and any callbacks have completed execution, or to
78- * access information from within the Lambda execution environment. Cannot be null.
85+ * @param context used to specify when the function and any callbacks have
86+ * completed execution, or to
87+ * access information from within the Lambda execution
88+ * environment. Cannot be null.
7989 * @return the response object
8090 * @throws IOException when unable to send the request
81- * @throws CustomResourceResponseException when unable to synthesize or serialize the response payload
91+ * @throws CustomResourceResponseException when unable to synthesize or
92+ * serialize the response payload
8293 */
8394 public HttpExecuteResponse send (CloudFormationCustomResourceEvent event ,
84- Context context ) throws IOException , CustomResourceResponseException {
95+ Context context ) throws IOException , CustomResourceResponseException {
8596 return send (event , context , null );
8697 }
8798
8899 /**
89- * Forwards a response containing a custom payload to the target resource specified by the event. The payload is
100+ * Forwards a response containing a custom payload to the target resource
101+ * specified by the event. The payload is
90102 * formed from the event, context, and response data.
91103 *
92104 * @param event custom CF resource event. Cannot be null.
93- * @param context used to specify when the function and any callbacks have completed execution, or to
94- * access information from within the Lambda execution environment. Cannot be null.
95- * @param responseData response to send, e.g. a list of name-value pairs. If null, an empty success is assumed.
105+ * @param context used to specify when the function and any callbacks have
106+ * completed execution, or to
107+ * access information from within the Lambda execution
108+ * environment. Cannot be null.
109+ * @param responseData response to send, e.g. a list of name-value pairs. If
110+ * null, an empty success is assumed.
96111 * @return the response object
97- * @throws IOException when unable to generate or send the request
98- * @throws CustomResourceResponseException when unable to serialize the response payload
112+ * @throws IOException when unable to generate or send the
113+ * request
114+ * @throws CustomResourceResponseException when unable to serialize the response
115+ * payload
99116 */
100117 public HttpExecuteResponse send (CloudFormationCustomResourceEvent event ,
101- Context context ,
102- Response responseData ) throws IOException , CustomResourceResponseException {
118+ Context context ,
119+ Response responseData ) throws IOException , CustomResourceResponseException {
103120 // no need to explicitly close in-memory stream
104121 StringInputStream stream = responseBodyStream (event , context , responseData );
105122 URI uri = URI .create (event .getResponseUrl ());
@@ -129,20 +146,23 @@ protected Map<String, List<String>> headers(int contentLength) {
129146 }
130147
131148 /**
132- * Returns the response body as an input stream, for supplying with the HTTP request to the custom resource.
149+ * Returns the response body as an input stream, for supplying with the HTTP
150+ * request to the custom resource.
133151 * <p>
134- * If PhysicalResourceId is null at this point it will be replaced with the Lambda LogStreamName.
152+ * If PhysicalResourceId is null at this point it will be replaced with the
153+ * Lambda LogStreamName.
135154 *
136- * @throws CustomResourceResponseException if unable to generate the response stream
155+ * @throws CustomResourceResponseException if unable to generate the response
156+ * stream
137157 */
138158 StringInputStream responseBodyStream (CloudFormationCustomResourceEvent event ,
139- Context context ,
140- Response resp ) throws CustomResourceResponseException {
159+ Context context ,
160+ Response resp ) throws CustomResourceResponseException {
141161 try {
142162 String reason = "See the details in CloudWatch Log Stream: " + context .getLogStreamName ();
143163 if (resp == null ) {
144- String physicalResourceId = event .getPhysicalResourceId () != null ? event .getPhysicalResourceId () :
145- context .getLogStreamName ();
164+ String physicalResourceId = event .getPhysicalResourceId () != null ? event .getPhysicalResourceId ()
165+ : context .getLogStreamName ();
146166
147167 ResponseBody body = new ResponseBody (event , Response .Status .SUCCESS , physicalResourceId , false , reason );
148168 LOG .debug ("ResponseBody: {}" , body );
@@ -152,12 +172,12 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event,
152172 if (!StringUtils .isBlank (resp .getReason ())) {
153173 reason = resp .getReason ();
154174 }
155- String physicalResourceId = resp .getPhysicalResourceId () != null ? resp .getPhysicalResourceId () :
156- event .getPhysicalResourceId () != null ? event .getPhysicalResourceId () :
157- context .getLogStreamName ();
175+ String physicalResourceId = resp .getPhysicalResourceId () != null ? resp .getPhysicalResourceId ()
176+ : event .getPhysicalResourceId () != null ? event .getPhysicalResourceId ()
177+ : context .getLogStreamName ();
158178
159- ResponseBody body =
160- new ResponseBody ( event , resp . getStatus (), physicalResourceId , resp . isNoEcho (), reason );
179+ ResponseBody body = new ResponseBody ( event , resp . getStatus (), physicalResourceId , resp . isNoEcho (),
180+ reason );
161181 LOG .debug ("ResponseBody: {}" , body );
162182 ObjectNode node = body .toObjectNode (resp .getJsonNode ());
163183 return new StringInputStream (node .toString ());
@@ -169,10 +189,14 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event,
169189 }
170190
171191 /**
172- * Internal representation of the payload to be sent to the event target URL. Retains all properties of the payload
173- * except for "Data". This is done so that the serialization of the non-"Data" properties and the serialization of
174- * the value of "Data" can be handled by separate ObjectMappers, if need be. The former properties are dictated by
175- * the custom resource but the latter is dictated by the implementor of the custom resource handler.
192+ * Internal representation of the payload to be sent to the event target URL.
193+ * Retains all properties of the payload
194+ * except for "Data". This is done so that the serialization of the non-"Data"
195+ * properties and the serialization of
196+ * the value of "Data" can be handled by separate ObjectMappers, if need be. The
197+ * former properties are dictated by
198+ * the custom resource but the latter is dictated by the implementor of the
199+ * custom resource handler.
176200 */
177201 @ SuppressWarnings ("unused" )
178202 static class ResponseBody {
@@ -189,10 +213,10 @@ static class ResponseBody {
189213 private final boolean noEcho ;
190214
191215 ResponseBody (CloudFormationCustomResourceEvent event ,
192- Response .Status responseStatus ,
193- String physicalResourceId ,
194- boolean noEcho ,
195- String reason ) {
216+ Response .Status responseStatus ,
217+ String physicalResourceId ,
218+ boolean noEcho ,
219+ String reason ) {
196220 Objects .requireNonNull (event , "CloudFormationCustomResourceEvent cannot be null" );
197221
198222 this .physicalResourceId = physicalResourceId ;
@@ -233,10 +257,13 @@ public boolean isNoEcho() {
233257 }
234258
235259 /**
236- * Returns this ResponseBody as an ObjectNode with the provided JsonNode as the value of its "Data" property.
260+ * Returns this ResponseBody as an ObjectNode with the provided JsonNode as the
261+ * value of its "Data" property.
237262 *
238- * @param dataNode the value of the "Data" property for the returned node; may be null
239- * @return an ObjectNode representation of this ResponseBody and the provided dataNode
263+ * @param dataNode the value of the "Data" property for the returned node; may
264+ * be null
265+ * @return an ObjectNode representation of this ResponseBody and the provided
266+ * dataNode
240267 */
241268 ObjectNode toObjectNode (JsonNode dataNode ) {
242269 ObjectNode node = MAPPER .valueToTree (this );
0 commit comments