-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Description
Since version 2.215.0, the interfaces of L2 functions like IBucket
, IFunction
, (etc) have gotten an additional member. If you are implementing these interfaces yourself that means you now have the obligation to implement a new member in order to satisfy the interface.
If you build applications using constructs from the AWS Construct Library (aws-cdk-lib
), or build higher-level L3 constructs using constructs from the Construct Library, you are not affected by this change and you can stop reading.
What is changing?
We have introduced new interfaces, called IBucketRef
, IFunctionRef
, (etc), that represent the minimal surface area of being a resource of a certain type that can be “referenced”, without any of the additional L2 baggage. L2 interfaces now extend this base interface.
This is what it looks like:
// This interface is new, representing the fact that an object represents
// a bucket that can be referenced.
interface IBucketRef {
// Return an object that describes the bucket name and bucket ARN
readonly bucketRef: BucketReference;
}
// This is the existing L2 interface that now also extends IBucketRef
interface IBucket extends IBucketRef {
// Handwritten functions here
readonly bucketWebsiteUrl: string;
arnForObjects(pattern?: string): string;
...
}
Taking IBucket
as an example, if you previously implemented IBucket
for your own classes, you have implemented bucketWebsiteUrl
, arnForObjects()
, and 32 other interface members.
You now need to implement bucketRef
as well. For example, like this:
class MyBucket implements IBucket {
// Implement bucketRef via a getter
public get bucketRef(): BucketReference {
return {
bucketName: this.bucketName,
bucketArn: this.bucketArn,
};
}
}
In 2.215.0 we are requiring these additional members for the services API Gateway, CloudFront, EC2, IAM, KMS, Lambda, and S3. The rest of the services will come in the next couple of weeks.
Why this change?
This is part of an initiative in CDK to improve the interoperability of L1 and L2 constructs. We are introducing a new interface with minimal requirements, so that L1s can implemented it as well. Through the introduction of a new interface that is implemented by both L1 and L2 constructs, it becomes possible to type constructs properties and function arguments in such a way that you can pass either an L1 or an L2 construct to set up the necessary relationships.
Let’s take the example of defining an ELBv2 Trust Store, which takes a Bucket as one of its parameters:
const l2Bucket = new s3.Bucket(this, 'L2Bucket');
const cfnBucket = new s3.CfnBucket(this, 'L1Bucket');
const trustStore = new elbv2.TrustStore(this, 'Store', {
// Before 2.215.0, this had to be an L2 bucket
bucket: l2Bucket,
// After 2.215.0, it's now possible to also pass an L1 CfnBucket
bucket: cfnBucket,
key: 'rootCA_cert.pem',
});
We are aware that the additional interface requirements may force some CDK construct authors who have chosen to implement L2 interfaces to do additional work to upgrade to the latest version of the CDK. We regret to have to force this on them, but given the relative rarity of doing that we think it’s the right choice to make this change to move the construct library forward and keeping the ecosystem flowing, rather than, say, declare a new major version and break the ecosystem of construct libraries into before-and-after libraries.
Interfaces and abstract classes in the AWS Construct Library are stable for consumers, but not for implementors. This means that interfaces like s3.IBucket
will never remove members, but periodically, new (abstract) members may be added to interfaces and abstract classes. Strictly treating additions to interfaces and abstract classes for implementors as breaking changes would unduly limit the evolvability of the AWS Construct Library. In most cases, implementors should prefer to extend concrete classes like s3.Bucket
.
How do I know if I’m affected?
When upgrading to a version of aws-cdk-lib >= 2.215.0
, you will see an error message like the following:
error TS2420: Class 'MyBucket' incorrectly implements interface 'IBucket'.
Property 'bucketRef' is missing in type 'MyBucket' but required in type 'IBucket'.
10 class MyBucket extends Resource implements IBucket {
~~~~~~~~
node_modules/aws-cdk-lib/aws-s3/lib/s3.generated.d.ts:265:14
265 readonly bucketRef: BucketReference;
~~~~~~~~~
'bucketRef' is declared here.
The error message you see may be for a different interface than IBucket
; if you get this error message about a different class and resource type, replace “Bucket” with your own resource name in the error message above and the instructions below.
What should I do?
Construct libraries or applications that implement an L2 interface should implement the additional member. From that point forward, they will only work with the AWS Construct Library version 2.214.1 or higher. TypeScript libraries should encode that fact in the peerDependency
field of package.json
.
1. Upgrading the AWS Construct library dependency
For TypeScript/JavaScript, update your package.json
to install your preferred version:
{
// Use "dependencies" for an application, pick a recent version
"dependencies": {
"aws-cdk-lib": "^2.219.0"
},
// Use "peerDependencies+devDependencies" for a construct library, pick the oldest possible version
"peerDependencies": {
"aws-cdk-lib": "^2.214.1"
},
"devDependencies": {
"aws-cdk-lib": "2.214.1"
}
}
For Python, update requirements.txt
for an application:
aws-cdk-lib==2.219.0
Or for a Python library, update setup.py
or your TOML file:
setup(
install_requires=[
'aws-cdk-lib>=2.214.1',
])
For Java, update pom.xml
:
<dependency>
<groupId>software.amazon.awscdk</groupId>
<artifactId>aws-cdk-lib</artifactId>
<version>2.214.1</version>
</dependency>
2. Implement the additional member
TypeScript: Implement the missing <resource>Ref
member, typically using a getter:
class MyBucket implements IBucket {
public get bucketRef(): BucketReference {
return {
bucketName: this.bucketName,
bucketArn: this.bucketArn,
};
}
}
Python: implement the missing <resource>_ref
member, typically using a property getter:
@jsii.implements(IBucket)
class MyBucket:
@property
def bucket_ref(self):
return BucketReference(
bucket_name=self.bucket_name,
bucket_arn=self.bucket_arn,
)
Java: implement the missing get<Resource>Ref
member:
class MyBucket implements IBucket {
BucketReference getBucketRef() {
return BucketReference.builder()
.bucketArn(getBucketArn())
.bucketName(getBucketName())
.build();
}
}
What should I do if I have a mix of packages that I need to migrate?
If you have multiple libraries that all implement L2 interfaces and now need the new member, you need to know the following:
- Classes without the new member will not work on
>= 2.215.0
(because the member needs to implemented) - Classes with the new member will not work anymore on
<2.214.1
(because the return type the member references doesn't exist yet).
A mix of libraries with and without the members will work together on exactly version 2.214.1.
That means you should upgrade your application to version 2.214.1 while you work on implementing the new member in all of your library packages. When all of your dependencies have implemented the new member, you are free to upgrade past 2.214.1 to any newer version.