aws-cdk: Incorrect/Insufficient Aspect Documentation
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the issue
The CDK documentation for Aspects doesn't seem to correctly demonstrate what is seen when running the code against a CDK app/stack. The documentation also doesn't clarify much of the purpose behind the use of code, and why it works (or how it can be generally applied to aspects). This issue might also extend to the Token documentation, but I don't know enough about the CDK to judge that.
I'm working with the AWS CDK in Java, so will focus on the Java Aspect examples in the given documentation. The documentation demonstrates Aspects by creating an Aspect that can validate S3 buckets have versioning enabled.
### Sample reproduction steps following the linked documentation:
1. Create an example BucketVersioningChecker aspect:
```
public class BucketVersioningChecker implements IAspect {
@Override
public void visit( @NotNull IConstruct node ) {
if ( node instanceof CfnBucket bucket ) {
Object versioningConfiguration = bucket.getVersioningConfiguration();
if ( versioningConfiguration == null
|| !Tokenization.isResolvable( versioningConfiguration.toString() )
&& !versioningConfiguration.toString().contains( "Enabled" ) ) {
Annotations.of( bucket ).addError( "S3 bucket does not have versioning enabled" );
}
}
}
}
```
2. Create a CDK stack that has an S3 bucket, and apply the aspect to the stack:
```
public class TestApp {
public static void main( final String[] args ) {
App app = new App()
Stack stack = new Stack(app, "stack");
Bucket bucket = Bucket.Builder.create(stack, "versionedBucket")
.versioned(true)
.build();
Aspects.of(stack).add(new BucketVersioningChecker());
app.synth()
}
}
```
3. Run a `cdk synth`
This returns the following error:
`[Error at /stack/versionedBucket/Resource] S3 bucket does not have versioning enabled`
After doing a lot of debugging (and trial and error), I've come across the following information:
- In the case of a non-versioned bucket, the `versioningConfiguration` comes back as `null` on the underlying `CfnBucket` resource, which is expected.
- However, for a versioned bucket, `bucket.getVersioningConfiguration()` comes back as a `JsiiObject` - this is OK, but there is no documentation that explains how we can manipulate/transform this `JsiiObject` into Java types.
Then, the example's use of `versioningConfiguration.toString()` is the default `Object.toString()` implementation, e.g., returns `software.amazon.jsii.JsiiObject@768fc0f2`. Even using `Tokenization.isResolvable(versioningConfiguration)` still returns `false` on a versioned bucket.
This also of course leads to the `contains()` condition always evaluating to false, and adding the error message to the bucket resource's annotations.
I have found through trial and error that resolving the JsiiObject in the following way works, but I don't know exactly why or if there are easier/better ways of doing so:
```
// returns a LinkedHashMap {status=Enabled}
Object resolved = Tokenization.resolve( versioningConfiguration , ResolveOptions.builder()
.scope( bucket )
.resolver( new DefaultTokenResolver( new StringConcat() ) )
.build() );
```
I found this was also required in order to create a similar Aspect that validates S3 buckets have some lifecycle policy applied (similar setup, except using `bucket.getLifecycleConfiguration()`).
Of course, this is a chunky way of having to resolve an object to a Java type (and I don't know enough about Jsii to know if this is a safe thing to do). Having to use this in any Aspects I create would be a slight pain, but could be centralised somewhere.
I can't for the life of me figure out whether I'm supposed to be checking things are resolvable constantly in Aspects (I'm very new to the CDK), but with this method it seems like any Aspect that inspects properties would need to do some form of JsiiObject conversion before being able to check anything.
### Missing documentation/information that would be helpful for aspect creation:
- What is the standard/recommended approach of converting `JsiiObject` into a readable Java type for use in Aspects?
- Why is `Tokenization.isResolvable()` required in the example aspect? Furthermore, what is the purpose of excluding the case where the property can be resolvable?
- Why do some 'empty' resource properties evaluate to a `JsiiObject` while others come back as `null`? For example, using `bucket.getLifecycleConfiguration()` returns a JsiiObject regardless of the configuration of the bucket. The difference is that the resulting `JsiiObject` will resolve to `null` if there are no lifecycle rules, and a `LinkedHashMap` otherwise.
### Links
https://docs.aws.amazon.com/cdk/v2/guide/aspects.html
Contributor guide
Research direction
Start with the linked AWS CDK Aspects guide and reproduce the Java BucketVersioningChecker example through cdk synth. Document the recommended handling of JsiiObject values, Tokenization.isResolvable(), and null versus resolvable properties, and ensure the example's behavior and rationale are clear for similar aspects.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, java
- Domain
- cloud, documentation, infrastructure
- Issue type
- Documentation
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100