Skip to content

Commit 0c612b6

Browse files
author
Joe Wolf
committed
Put Response body attributes (status, noEcho, etc) in Response builder itself
Instead of method args to various CloudFormationResponse::send methods, reducing the number of polymorphic send methods to two: one with a Response arg and one without. Rename ResponseException to CustomResourceResponseException. AbstractCustomResourceHandlerTest simplifications.
1 parent 7abe871 commit 0c612b6

File tree

9 files changed

+431
-261
lines changed

9 files changed

+431
-261
lines changed

powertools-cloudformation/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@
7474
<artifactId>junit-jupiter-engine</artifactId>
7575
<scope>test</scope>
7676
</dependency>
77+
<dependency>
78+
<groupId>org.junit.jupiter</groupId>
79+
<artifactId>junit-jupiter-params</artifactId>
80+
<scope>test</scope>
81+
</dependency>
7782
<dependency>
7883
<groupId>org.mockito</groupId>
7984
<artifactId>mockito-core</artifactId>

powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
88
import software.amazon.awssdk.http.SdkHttpClient;
9-
import software.amazon.lambda.powertools.cloudformation.CloudFormationResponse.ResponseStatus;
109

1110
import java.io.IOException;
1211
import java.util.Objects;
@@ -51,26 +50,26 @@ public final Response handleRequest(CloudFormationCustomResourceEvent event, Con
5150
try {
5251
response = getResponse(event, context);
5352
LOG.debug("Preparing to send response {} to {}.", response, responseUrl);
54-
client.send(event, context, ResponseStatus.SUCCESS, response);
53+
client.send(event, context, response);
5554
} catch (IOException ioe) {
5655
LOG.error("Unable to send {} success to {}.", responseUrl, ioe);
5756
onSendFailure(event, context, response, ioe);
58-
} catch (ResponseException rse) {
57+
} catch (CustomResourceResponseException rse) {
5958
LOG.error("Unable to create/serialize Response. Sending empty failure to {}", responseUrl, rse);
6059
// send a failure with a null response on account of response serialization issues
6160
try {
62-
client.send(event, context, ResponseStatus.FAILED);
61+
client.send(event, context, Response.failed());
6362
} catch (Exception e) {
6463
// unable to serialize response AND send an empty response
65-
LOG.error("Unable to send failure to {}.", responseUrl, e);
64+
LOG.error("Unable to send empty failure to {}.", responseUrl, e);
6665
onSendFailure(event, context, null, e);
6766
}
6867
}
6968
return response;
7069
}
7170

7271
private Response getResponse(CloudFormationCustomResourceEvent event, Context context)
73-
throws ResponseException {
72+
throws CustomResourceResponseException {
7473
try {
7574
switch (event.getRequestType()) {
7675
case "Create":
@@ -84,7 +83,7 @@ private Response getResponse(CloudFormationCustomResourceEvent event, Context co
8483
return null;
8584
}
8685
} catch (RuntimeException e) {
87-
throw new ResponseException("Unable to get Response", e);
86+
throw new CustomResourceResponseException("Unable to get Response", e);
8887
}
8988
}
9089

powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java

+34-94
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,6 @@
3232
*/
3333
public class CloudFormationResponse {
3434

35-
/**
36-
* Indicates if the function invoking send had successfully completed.
37-
*/
38-
public enum ResponseStatus {
39-
SUCCESS, FAILED
40-
}
41-
4235
/**
4336
* Internal representation of the payload to be sent to the event target URL. Retains all properties of the payload
4437
* except for "Data". This is done so that the serialization of the non-"Data" properties and the serialization of
@@ -61,14 +54,14 @@ static class ResponseBody {
6154

6255
ResponseBody(CloudFormationCustomResourceEvent event,
6356
Context context,
64-
ResponseStatus responseStatus,
57+
Response.Status responseStatus,
6558
String physicalResourceId,
6659
boolean noEcho) {
6760
Objects.requireNonNull(event, "CloudFormationCustomResourceEvent cannot be null");
6861
Objects.requireNonNull(context, "Context cannot be null");
6962
this.physicalResourceId = physicalResourceId != null ? physicalResourceId : context.getLogStreamName();
7063
this.reason = "See the details in CloudWatch Log Stream: " + context.getLogStreamName();
71-
this.status = responseStatus == null ? ResponseStatus.SUCCESS.name() : responseStatus.name();
64+
this.status = responseStatus == null ? Response.Status.SUCCESS.name() : responseStatus.name();
7265
this.stackId = event.getStackId();
7366
this.requestId = event.getRequestId();
7467
this.logicalResourceId = event.getLogicalResourceId();
@@ -132,95 +125,38 @@ public CloudFormationResponse(SdkHttpClient client) {
132125
}
133126

134127
/**
135-
* Forwards a request containing a custom payload to the target resource specified by the event. The payload is
136-
* formed from the event and context data.
128+
* Forwards a response containing a custom payload to the target resource specified by the event. The payload is
129+
* formed from the event and context data. Status is assumed to be SUCCESS.
137130
*
138-
* @param event custom CF resource event
131+
* @param event custom CF resource event. Cannot be null.
139132
* @param context used to specify when the function and any callbacks have completed execution, or to
140-
* access information from within the Lambda execution environment.
141-
* @param status whether the function successfully completed
133+
* access information from within the Lambda execution environment. Cannot be null.
142134
* @return the response object
143-
* @throws IOException when unable to generate or send the request
144-
* @throws ResponseException when unable to serialize the response payload
135+
* @throws IOException when unable to send the request
136+
* @throws CustomResourceResponseException when unable to synthesize or serialize the response payload
145137
*/
146138
public HttpExecuteResponse send(CloudFormationCustomResourceEvent event,
147-
Context context,
148-
ResponseStatus status) throws IOException, ResponseException {
149-
return send(event, context, status, null);
139+
Context context) throws IOException, CustomResourceResponseException {
140+
return send(event, context, null);
150141
}
151142

152143
/**
153-
* Forwards a request containing a custom payload to the target resource specified by the event. The payload is
154-
* formed from the event, status, and context data.
144+
* Forwards a response containing a custom payload to the target resource specified by the event. The payload is
145+
* formed from the event, context, and response data.
155146
*
156-
* @param event custom CF resource event
147+
* @param event custom CF resource event. Cannot be null.
157148
* @param context used to specify when the function and any callbacks have completed execution, or to
158-
* access information from within the Lambda execution environment.
159-
* @param status whether the function successfully completed
160-
* @param responseData response to send, e.g. a list of name-value pairs. May be null.
149+
* access information from within the Lambda execution environment. Cannot be null.
150+
* @param responseData response to send, e.g. a list of name-value pairs. If null, an empty success is assumed.
161151
* @return the response object
162-
* @throws IOException when unable to generate or send the request
163-
* @throws ResponseException when unable to serialize the response payload
152+
* @throws IOException when unable to generate or send the request
153+
* @throws CustomResourceResponseException when unable to serialize the response payload
164154
*/
165155
public HttpExecuteResponse send(CloudFormationCustomResourceEvent event,
166156
Context context,
167-
ResponseStatus status,
168-
Response responseData) throws IOException, ResponseException {
169-
return send(event, context, status, responseData, null);
170-
}
171-
172-
/**
173-
* Forwards a request containing a custom payload to the target resource specified by the event. The payload is
174-
* formed from the event, status, response, and context data.
175-
*
176-
* @param event custom CF resource event
177-
* @param context used to specify when the function and any callbacks have completed execution, or to
178-
* access information from within the Lambda execution environment.
179-
* @param status whether the function successfully completed
180-
* @param responseData response to send, e.g. a list of name-value pairs. May be null.
181-
* @param physicalResourceId Optional. The unique identifier of the custom resource that invoked the function. By
182-
* default, the module uses the name of the Amazon CloudWatch Logs log stream that's
183-
* associated with the Lambda function.
184-
* @return the response object
185-
* @throws IOException when unable to send the request
186-
* @throws ResponseException when unable to serialize the response payload
187-
*/
188-
public HttpExecuteResponse send(CloudFormationCustomResourceEvent event,
189-
Context context,
190-
ResponseStatus status,
191-
Response responseData,
192-
String physicalResourceId) throws IOException, ResponseException {
193-
return send(event, context, status, responseData, physicalResourceId, false);
194-
}
195-
196-
/**
197-
* Forwards a request containing a custom payload to the target resource specified by the event. The payload is
198-
* formed from the event, status, response, and context data.
199-
*
200-
* @param event custom CF resource event
201-
* @param context used to specify when the function and any callbacks have completed execution, or to
202-
* access information from within the Lambda execution environment.
203-
* @param status whether the function successfully completed
204-
* @param responseData response to send, e.g. a list of name-value pairs. May be null.
205-
* @param physicalResourceId Optional. The unique identifier of the custom resource that invoked the function. By
206-
* default, the module uses the name of the Amazon CloudWatch Logs log stream that's
207-
* associated with the Lambda function.
208-
* @param noEcho Optional. Indicates whether to mask the output of the custom resource when it's
209-
* retrieved by using the Fn::GetAtt function. If set to true, all returned values are
210-
* masked with asterisks (*****), except for information stored in the locations specified
211-
* below. By default, this value is false.
212-
* @return the response object
213-
* @throws IOException when unable to send the request
214-
* @throws ResponseException when unable to serialize the response payload
215-
*/
216-
public HttpExecuteResponse send(CloudFormationCustomResourceEvent event,
217-
Context context,
218-
ResponseStatus status,
219-
Response responseData,
220-
String physicalResourceId,
221-
boolean noEcho) throws IOException, ResponseException {
157+
Response responseData) throws IOException, CustomResourceResponseException {
222158
// no need to explicitly close in-memory stream
223-
StringInputStream stream = responseBodyStream(event, context, status, responseData, physicalResourceId, noEcho);
159+
StringInputStream stream = responseBodyStream(event, context, responseData);
224160
URI uri = URI.create(event.getResponseUrl());
225161
SdkHttpRequest request = SdkHttpRequest.builder()
226162
.uri(uri)
@@ -250,20 +186,24 @@ protected Map<String, List<String>> headers(int contentLength) {
250186
/**
251187
* Returns the response body as an input stream, for supplying with the HTTP request to the custom resource.
252188
*
253-
* @throws ResponseException if unable to generate the response stream
189+
* @throws CustomResourceResponseException if unable to generate the response stream
254190
*/
255-
private StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event,
256-
Context context,
257-
ResponseStatus status,
258-
Response responseData,
259-
String physicalResourceId,
260-
boolean noEcho) throws ResponseException {
191+
StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event,
192+
Context context,
193+
Response resp) throws CustomResourceResponseException {
261194
try {
262-
ResponseBody body = new ResponseBody(event, context, status, physicalResourceId, noEcho);
263-
ObjectNode node = body.toObjectNode(responseData == null ? null : responseData.getJsonNode());
264-
return new StringInputStream(node.toString());
195+
if (resp == null) {
196+
ResponseBody body = new ResponseBody(event, context, Response.Status.SUCCESS, null, false);
197+
ObjectNode node = body.toObjectNode(null);
198+
return new StringInputStream(node.toString());
199+
} else {
200+
ResponseBody body = new ResponseBody(
201+
event, context, resp.getStatus(), resp.getPhysicalResourceId(), resp.isNoEcho());
202+
ObjectNode node = body.toObjectNode(resp.getJsonNode());
203+
return new StringInputStream(node.toString());
204+
}
265205
} catch (RuntimeException e) {
266-
throw new ResponseException("Unable to generate response body.", e);
206+
throw new CustomResourceResponseException("Unable to generate response body.", e);
267207
}
268208
}
269209
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package software.amazon.lambda.powertools.cloudformation;
2+
3+
/**
4+
* Indicates an error while generating or serializing a response to be sent to a custom resource.
5+
*/
6+
public class CustomResourceResponseException extends Exception {
7+
8+
private static final long serialVersionUID = 20211004;
9+
10+
protected CustomResourceResponseException(String message, Throwable cause) {
11+
super(message, cause);
12+
}
13+
}

0 commit comments

Comments
 (0)