-
Notifications
You must be signed in to change notification settings - Fork 89
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
feat: create Java cfn-response equivalent (#558) #560
Conversation
Is the SpotBugs / codecheck failure a false positive? The logs do not show any violations for the impacted module: |
A couple of questions from me.
|
Another design issue for your consideration: Right now there's a single For my own application, e.g. I would ideally like to pass a
|
Yeah I have been trying to figure this one out. It looks like it fails on PRs where author donot have access to repo. I need to find some time to troubleshoot this further. |
...n/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java
Outdated
Show resolved
Hide resolved
* | ||
* @param client HTTP client to use for sending requests; cannot be null | ||
*/ | ||
public CloudFormationResponse(SdkHttpClient client) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be good to provide an implementation where user dont need to care about passing http client as well and module can take care of setting it up.
From Custom resource perspective, I believe it will provide better developer experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. The JS and Python versions embed the client. We'd just need to
- Pick a particular client impl (Apache?) and
- Include it as a dependency to the project.
@bdkosher Firstly, Thanks for opening the issue and PR. I am thinking out loud here but will it be better builder experience if we provide abstract implementation like this:
Then from builder perspective, we can control alot of things, for example, Also then builders wont need to know too much details about how to handle custom resource events etc. From their perspective, they just need to do their stuff for create, update and delete and provide us a response object. What do you think? I believe this is closer to the implementation we have for python one. Builder experience then looks something like this:
|
Seems like a good idea; as far as I know, CloudFormationResponse only gets used in the context of a handler that does some sort of one-time stack setup action based on the event type. So if I understand you correctly, we'd add two more classes:
I'm debating between having void sendResponse(event, context, status, CreateAppInstanceResponse chimeResponse) {
// easier to do this than configure a mapper to serialize artifact object type
Map<String, String> data = Map.of("AppInstanceArn", chimeResponse.appInstanceArn());
new CloudFormationResponse(ApacheHttpClient.create()).send(event, context, status, data);
} |
Idea was to basically let users return a custom response object which we provide with builders etc. Users can decide based on the processing what will be the status etc. But basically we accept most of the parameters (Some optional probably) which users would want to send in response. This will include a custom object as well as part of response. The response object can then as well provide a method to accept custom object mapper which they would probably want to use when serializing response object. Builder dont need to then worry about sending the repsonse itself. That will be taken care by the handler which we implement as part of |
It might be an idea to give an optional hook which is called in case send response invocation fails. Although I wonder what would user do in that case ? But still it might be a good idea to provide that flexibility . |
That was my thought yes. So Builder just need to provide implementation of three methods and return the response object. |
I believe it can accept any object, which could be a map as well if they want it to be. We can document that by default it will be serialized using default instance of object mapper, but if they want to use specific object mapper configuration, they can provide one as part of the custom Response object. |
Try rebasing with master and that should now fix spotbugs check issue for now. |
Pushed some changes for your consideration. I added the abstract resource handler and Response classes (with Builder) as we discussed, as well as a
I'm not fond of the the nested try/catches being used to handle these two failure scenarios--maybe there's a way to refactor If there's an exception while sending the response, I'm currently just logging it and invoking the empty-by-default A final note, I'm still requiring the |
try { | ||
response = getResponse(event, context); | ||
LOG.debug("Preparing to send response {} to {}.", response, responseUrl); | ||
client.send(event, context, ResponseStatus.SUCCESS, response); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be its not rite to assume that user will always like to send status as Success ? Probably Response
object should accept status as a field too and defaulting it to success ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this. We could also even allow the physical resource ID and noEcho flag to be specified in the Response. Otherwise, with the way the handler is set up, there's no way to customize these values outside of invoking CloudFormationResponse::send
directly from a custom handler.
Will still send success if the entire response is null; returning null from the create/update/delete methods is basically saying "I don't care about these type of of events".
* <p> | ||
* This class is thread-safe provided the SdkHttpClient instance used is also thread-safe. | ||
*/ | ||
public class CloudFormationResponse { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the implmentation here should now be internal ? and not be public as users only need to care about providing implementation for the abstract class ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with that. I had left it public in case anyone wanted to write their own custom handler, but given the nature of this code, I can't come up with a good reason why anyone would actually do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still believe it's better to close it down. See it's easier to open it up based on community feedback and hearing use cases around it later than starting with an open one. Then its impossible to close it ever.
...mation/src/main/java/software/amazon/lambda/powertools/cloudformation/ResponseException.java
Outdated
Show resolved
Hide resolved
...java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java
Outdated
Show resolved
Hide resolved
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
public class AbstractCustomResourceHandlerTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the test be simpler to just verify and argument captor on mocked http client calls and arguments ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. Verifying the call to SdkHttpClient::prepareRequest
is tricky because the HttpExecuteRequest arg doesn't expose readily-usable methods for validating things, but using Mockito to verify calls to create/update/delete on the handler itself would be more straightforward that manually counting invocations.
This is great. Already starting to look nice. Left few suggestions, see if you agree with them. |
Pushed another commit taking most of your feedback into consideration.
|
I think everything looks much better. This is seems to shape up real nice now. I still believe it's better to close For Default client, I think especially for the use case of Custom resource, Cold start is not a big problem, so if I have to choose taking an extra dep over better builder experience and ease of use, I would say latter wins :) |
In addition, I believe you also need to do changes here https://github.com/awslabs/aws-lambda-powertools-java/blob/master/.github/workflows/build.yml and few other files in .github folder for automation to work correctly for this module. |
Makes sense. Will update it to be package-private.
True. OK with "software.amazon.awssdk:url-connection-client" as the default client? Looks to be the smallest client size-wise.
OK. Looks like that file and spotbugs.yml. Also, do you see value in making 3 separate handler sub-classes each specific to one operation, e.g. |
I would avoid doing this, One reason is to make it visible to customer that there are 3 parts to the puzzle of implementing a custom resource, so that its rite there for them to decide if they want to do anything on create or update or delete or not. Other is, Even though it will ease use and make code cleaner for ones who dont need it and know about it, I would avoid adding it until i see community ask as of now. |
Sounds good |
0c612b6
to
9e09e7d
Compare
Pushed another commit with the .github config, default SdkClient, lowered CloudFormationResource visibility, and a spotbugs fix. Provided things look good to you and the automated checks pass, I'll start the documentation. |
Looks good to me. Probably we can start with documentation :) |
Missing entry of docs here https://github.com/awslabs/aws-lambda-powertools-java/blob/master/mkdocs.yml#L15 |
Looked through the docs and looks good. |
Thanks for all the feedback. I'll push this missing commits from master to remove the pom.xml conflict. |
Introduces Java implementation of cfn-response module based on NodeJS design. Put the sole class in a separate powertools-cloudformation Maven module. 100% code coverage Addresses aws-powertools#558
Provides abstract methods for generating the Response to be sent, represented as its own type. Use Objects::nonNull instead of custom method. Addresses aws-powertools#558
…r 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.
- Include powertools-cloudformation in .github config - Address mutatable ObjectMapper spotbugs finding - JavaDoc cleanup
15af6f9
to
3627d4d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to go. Thanks for all the efforts @bdkosher 🙌
Introduce Java implementation of the cfn-response module based on NodeJS design.
Issue #, if available:
Addresses #558
Description of changes:
CloudFormationResponse
.software.amazon.awssdk:http-client-spi:${aws.sdk.version}
.Checklist
Breaking change checklist
None. New code.
RFC issue #:
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.