Skip to content

Commit

Permalink
Merge branch 'master' into robertd/autoscaling-step-scaling-policy
Browse files Browse the repository at this point in the history
  • Loading branch information
robertd authored Mar 2, 2021
2 parents b0955a7 + d884a34 commit f792432
Show file tree
Hide file tree
Showing 207 changed files with 1,312 additions and 345 deletions.
309 changes: 284 additions & 25 deletions DESIGN_GUIDELINES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
# AWS Construct Library Design Guidelines

- [AWS Construct Library Design Guidelines](#aws-construct-library-design-guidelines)
- [What's Included](#what-s-included)
- [API Design](#api-design)
- [Modules](#modules)
- [Construct Class](#construct-class)
- [Construct Interface](#construct-interface)
- [Owned vs. Unowned Constructs](#owned-vs-unowned-constructs)
- [Abstract Base](#abstract-base)
- [Props](#props)
- [Types](#types)
- [Defaults](#defaults)
- [Flat](#flat)
- [Concise](#concise)
- [Naming](#naming)
- [Property Documentation](#property-documentation)
- [Enums](#enums)
- [Unions](#unions)
- [Attributes](#attributes)
- [Configuration](#configuration)
- [Prefer Additions](#prefer-additions)
- [Dropped Mutations](#dropped-mutations)
- [Factories](#factories)
- [Imports](#imports)
- [“from” Methods](#-from--methods)
- [From-attributes](#from-attributes)
- [Roles](#roles)
- [Resource Policies](#resource-policies)
- [VPC](#vpc)
- [Grants](#grants)
- [Metrics](#metrics)
- [Events](#events)
- [Connections](#connections)
- [Integrations](#integrations)
- [State](#state)
- [Physical Names - TODO](#physical-names---todo)
- [Tags](#tags)
- [Secrets](#secrets)
- [Project Structure](#project-structure)
- [Code Organization](#code-organization)
- [Implementation](#implementation)
- [General Principles](#general-principles)
- [Construct IDs](#construct-ids)
- [Errors](#errors)
- [Input Validation](#input-validation)
- [Avoid Errors If Possible](#avoid-errors-if-possible)
- [Never Catch Exceptions](#never-catch-exceptions)
- [Post Validation](#post-validation)
- [Attached Errors/Warnings](#attached-errors-warnings)
- [Tokens](#tokens)
- [Documentation](#documentation)
- [Inline Documentation](#inline-documentation)
- [Readme](#readme)
- [Testing](#testing)
- [Unit tests](#unit-tests)
- [Integration tests](#integration-tests)
- [Versioning](#versioning)
- [Naming & Style](#naming---style)
- [Naming Conventions](#naming-conventions)
- [Coding Style](#coding-style)

The AWS Construct Library is a rich class library of CDK constructs which
represent all resources offered by the AWS Cloud and higher-level constructs for
achieving common tasks.
Expand All @@ -8,6 +68,8 @@ The purpose of this document is to provide guidelines for designing the APIs in
the AWS Construct Library in order to ensure a consistent and integrated
experience across the entire AWS surface area.

## Preface

As much as possible, the guidelines in this document are enforced using the
[**awslint** tool](https://www.npmjs.com/package/awslint) which reflects on the
APIs and verifies that the APIs adhere to the guidelines. When a guideline is
Expand Down Expand Up @@ -56,6 +118,73 @@ allows the library to be used from all supported programming languages. jsii
poses restrictions on language features that cannot be idiomatically represented
in target languages.

## What's Included

The AWS Construct Library, which is shipped as part of the AWS CDK constructs
representing AWS resources.

The AWS Construct Library has multiple layers of constructs, beginning
with low-level constructs, which we call _CFN Resources_ (or L1, short for
"level 1") or CFN Resources (short for CloudFormation). These constructs
directly represent all resources available in AWS CloudFormation. CFN Resources
are periodically generated from the AWS CloudFormation Resource
Specification. They are named **Cfn**_Xyz_, where _Xyz_ is name of the
resource. For example, CfnBucket represents the AWS::S3::Bucket AWS
CloudFormation resource. When you use Cfn resources, you must explicitly
configure all resource properties, which requires a complete understanding of
the details of the underlying AWS CloudFormation resource model.

The next level of constructs, L2, also represent AWS resources, but with a
higher-level, intent-based API. They provide similar functionality, but provide
the defaults, boilerplate, and glue logic you'd be writing yourself with a CFN
Resource construct. L2 constructs offer convenient defaults and reduce the need
to know all the details about the AWS resources they represent, while providing
convenience methods that make it simpler to work with the resource. For example,
the `s3.Bucket` class represents an Amazon S3 bucket with additional properties
and methods, such as `bucket.addLifeCycleRule()`, which adds a lifecycle rule to
the bucket.

Examples of behaviors that an L2 commonly include:

- Strongly-typed modeling of the underlying L1 properties
- Methods for integrating other AWS resources (e.g., adding an event notification to
an S3 bucket).
- Modeling of permissions and resource policies
- Modeling of metrics

In addition to the above, some L2s may introduce more complex and
helpful functionality, either part of the original L2 itself, or as part of a
separate construct. The most common form of these L2s are integration constructs
that model interactions between different services (e.g., SNS publishing to SQS,
CodePipeline actions that trigger Lambda functions).

The next level of abstraction present within the CDK are what we designate as
"L2.5s": a step above the L2s in terms of abstraction, but not quite at the
level of complete patterns or applications. These constructs still largely
focus on a single logical resource -- in constrast to "patterns" which combine
multiple resources -- but are customized for a specific common usage scenario of
an L2. Examples of L2.5s in the CDK are `aws-apigateway.LambdaRestApi`,
`aws-lambda-nodejs.NodeJsFunction`, `aws-rds.ServerlessCluster` and `eks.FargateCluster`.

L2.5 constructs will be considered for inclusion in the CDK if they...

- cover a common usage scenario that can be used by a significant portion of
the community;
- provide significant ease of use over the base L2 (via usage-specific defaults
convenience methods or improved strong-typing);
- simplify or enable another L2 within the CDK

The CDK also currently includes some even higher-level constructs, which we call
patterns. These constructs often involve multiple kinds of resources and are
designed to help you complete common tasks in AWS or represent entire
applications. For example, the
`aws-ecs-patterns.ApplicationLoadBalancedFargateService` construct represents an
architecture that includes an AWS Fargate container cluster employing an
Application Load Balancer (ALB). These patterns are typically difficult to
design to be one-size-fits-all and are best suited to be published as separate
libraries, rather than included directly in the CDK. The patterns that currently
exist in the CDK will be removed in the next CDK major version (CDKv2).

## API Design

### Modules
Expand Down Expand Up @@ -333,14 +462,31 @@ from harnessing the full power of the resource, and customizing its behavior.
alignment.

The **@default** documentation tag must be included on all optional properties
of interfaces. Since there are cases where the default behavior is not a
specific value but rather depends on circumstances/context, the default
documentation tag must always begin with a “**-**" and then include a
description of the default behavior _[awslint:props-default-doc]_.
of interfaces.

In cases where the default behavior can be described by a value (typically the
case for booleans and enums, sometimes for strings and numbers), the value immediately
follows the **@default** tag and should be a valid JavaScript value (as in:
`@default false`, or `@default "stringValue"`).

For example:
In the majority of cases, the default behavior is not a specific value but
rather depends on circumstances/context. The default documentation tag must
begin with a “**-**" and then include a description of the default behavior
_[awslint:props-default-doc]_. This is specially true if the property
is a complex value or a reference to an object: don't write `@default
undefined`, describe the behavior that happens if the property is not
supplied.

Describe the default value or default behavior, even if it's not CDK that
controls the default. For example, if an absent value does not get rendered
into the template and it's ultimately the AWS *service* that determines the
default behavior, we still describe it in our documentation.

Examples:

```ts
// ✅ DO - uses a '-' and describes the behavior

/**
* External KMS key to use for bucket encryption.
*
Expand All @@ -350,6 +496,32 @@ For example:
encryptionKey?: kms.IEncryptionKey;
```

```ts
/**
* External KMS key to use for bucket encryption.
*
* @default undefined
* ❌ DO NOT - that the value is 'undefined' by default is implied. However,
* what will the *behavior* be if the value is left out?
*/
encryptionKey?: kms.IEncryptionKey;
```

```ts
/**
* Minimum capacity of the AutoScaling resource
*
* @default - no minimum capacity
* ❌ DO NOT - there most certainly is. It's probably 0 or 1.
*
* // OR
* @default - the minimum capacity is the default minimum capacity
* ❌ DO NOT - this is circular and useless to the reader.
* Describe what will actually happen.
*/
minCapacity?: number;
```

#### Flat

Do not introduce artificial nesting for props. It hinders discoverability and
Expand Down Expand Up @@ -1216,19 +1388,6 @@ for (const az of availabilityZones) {
### Errors
#### Input Validation
Prefer to validate input as early as it is passed into your code (ctor, methods,
etc) and bail out by throwing an **Error** (no need to create subclasses of
Error since all errors in the CDK are unrecoverable):
* All lowercase sentences (usually they are printed after “Error: \<message\>”)
* Include a descriptive message
* Include the value provided
* Include the expected/allowed values
* No need to include information that can be obtained from the stack trace
* No need to add a period at the end of error messages
#### Avoid Errors If Possible
Always prefer to do the right thing for the user instead of raising an
Expand All @@ -1237,26 +1396,126 @@ example, VPC has **enableDnsHostnames** and **enableDnsSupport**. DNS hostnames
*require* DNS support, so only fail if the user enabled DNS hostnames but
explicitly disabled DNS support. Otherwise, auto-enable DNS support for them.
#### Error reporting mechanism
There are three mechanism you can use to report errors:
* Eagerly throw an exception (fails synthesis)
* Attach a (lazy) validator to a construct (fails synthesis)
* Attach errors to a construct (succeeds synthesis, fails deployment)
Between these, the first two fail synthesis, while the latter doesn't. Failing synthesis
means that no Cloud Assembly will be produced.
The distinction becomes apparent when you consider multiple stacks in the same Cloud
Assembly:
* If synthesis fails due to an error in *one* stack (either by throwing an exception
or by failing validation), the other stack can also not be deployed.
* In contrast, if you attach an error to a construct in one stack, that stack cannot
be deployed but the other one still can.
Choose one of the first two methods if the failure is caused by a misuse of the API,
which the user should be alerted to and fix as quickly as possible. Choose attaching
an error to a construct if the failure is due to environmental factors outside the
direct use of the API surface (for example, lack of context provider lookup values).
#### Throwing exceptions
This should be the preferred error reporting method.
Validate input as early as it is passed into your code (ctor, methods,
etc) and bail out by throwing an `Error`. No need to create subclasses of
Error since all errors in the CDK are unrecoverable.
When validating inputs, don't forget to account for the fact that these
values may be `Token`s and not available for inspection at synthesis time.
Example:
```ts
if (!Token.isUnresolved(props.minCapacity) && props.minCapacity < 1) {
throw new Error(`'minCapacity' should be at least 1, got '${props.minCapacity}'`);
}
```
#### Never Catch Exceptions
All CDK errors are unrecoverable. If a method wishes to signal a recoverable
All CDK errors are unrecoverable. If a method wishes to signal a recoverable
error, this should be modeled in a return value and not through exceptions.
#### Post Validation
#### Attaching (lazy) Validators
In the rare case where the integrity of your construct can only be checked
after the app has completed its initialization, call the
**this.node.addValidation()** method to add a validation object. This will
generally only be necessary if you want to produce an error when a certain
interaction with your construct did *not* happen (for example, a property
that should have been configured over the lifetime of the construct, wasn't):
Always prefer early input validation over post-validation, as the necessity
of these should be rare.
Example:
In the rare case where the integrity of your construct can only be checked right
before synthesis, override the **Construct.validate()** method and return
meaningful errors. Always prefer early input validation over post-validation.
```ts
this.node.addValidation({
// 'validate' should return a string[] list of errors
validate: () => this.rules.length === 0
? ['At least one Rule must be added. Call \'addRule()\' to add Rules.']
: []
}
});
```
#### Attached Errors/Warnings
#### Attaching Errors/Warnings
You can also “attach” an error or a warning to a construct via
the **Annotations** class. These methods (e.g., `Annotations.of(construct).addWarning`)
will attach CDK metadata to your construct, which will be displayed to the user
by the toolchain when the stack is deployed.
Errors will not allow deployment and warnings will only be displayed in
highlight (unless **--strict** mode is used).
highlight (unless `--strict` mode is used).
```ts
if (!Token.isUnresolved(subnetIds) && subnetIds.length < 2) {
Annotations.of(this).addError(`Need at least 2 subnet ids, got: ${JSON.stringify(subnetIds)}`);
}
```
#### Error messages
Think about error messages from the point of view of the end user of the CDK.
This is not necessarily someone who knows about the internals of your
construct library, so try to phrase the message in a way that would make
sense to them.
For example, if a value the user supplied gets handed off between a number of
functions before finally being validated, phrase the message in terms of the
API the user interacted with, not in terms of the internal APIs.
A good error message should include the following components:
* What went wrong, in a way that makes sense to a top-level user
* An example of the incorrect value provided (if applicable)
* An example of the expected/allowed values (if applicable)
* The message should explain the (most likely) cause and change the user can
make to rectify the situation
The message should be all lowercase and not end in a period, or contain
information that can be obtained from the stack trace.
```ts
// ✅ DO - show the value you got and be specific about what the user should do
`supply at least one of minCapacity or maxCapacity, got ${JSON.stringify(action)}`

// ❌ DO NOT - this tells the user nothing about what's wrong or what they should do
`required values are missing`

// ❌ DO NOT - this error only makes sense if you know the implementation
`'undefined' is not a number`
```
### Tokens
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
"fs-extra": "^9.1.0",
"graceful-fs": "^4.2.6",
"jest-junit": "^12.0.0",
"jsii-diff": "^1.21.0",
"jsii-pacmak": "^1.21.0",
"jsii-rosetta": "^1.21.0",
"jsii-diff": "^1.23.0",
"jsii-pacmak": "^1.23.0",
"jsii-rosetta": "^1.23.0",
"lerna": "^3.22.1",
"standard-version": "^9.1.1",
"typescript": "~3.9.9"
Expand Down
3 changes: 3 additions & 0 deletions packages/@aws-cdk/alexa-ask/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"Framework :: AWS CDK",
"Framework :: AWS CDK :: 1"
]
},
"go": {
"moduleName": "github.com/aws/aws-cdk-go"
}
},
"projectReferences": true
Expand Down
Loading

0 comments on commit f792432

Please sign in to comment.