diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index 98fbf49c04bcc..0888dfe57f4bb 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -36,6 +36,10 @@ const WRITE_DATA_ACTIONS = [ 'dynamodb:DeleteItem', ]; +/** + * Represents an attribute for describing the key schema for the table + * and indexes. + */ export interface Attribute { /** * The name of an attribute. @@ -48,6 +52,11 @@ export interface Attribute { readonly type: AttributeType; } +/** + * Properties of a DynamoDB Table + * + * Use {@link TableProps} for all table properties + */ export interface TableOptions { /** * Partition key attribute definition. @@ -129,6 +138,9 @@ export interface TableOptions { readonly replicationRegions?: string[]; } +/** + * Properties for a DynamoDB Table + */ export interface TableProps extends TableOptions { /** * Enforces a particular physical table name. @@ -137,6 +149,9 @@ export interface TableProps extends TableOptions { readonly tableName?: string; } +/** + * Properties for a secondary index + */ export interface SecondaryIndexProps { /** * The name of the secondary index. @@ -156,6 +171,9 @@ export interface SecondaryIndexProps { readonly nonKeyAttributes?: string[]; } +/** + * Properties for a global secondary index + */ export interface GlobalSecondaryIndexProps extends SecondaryIndexProps { /** * The attribute of a partition key for the global secondary index. @@ -187,6 +205,9 @@ export interface GlobalSecondaryIndexProps extends SecondaryIndexProps { readonly writeCapacity?: number; } +/** + * Properties for a local secondary index + */ export interface LocalSecondaryIndexProps extends SecondaryIndexProps { /** * The attribute of a sort key for the local secondary index. @@ -1110,6 +1131,11 @@ export class Table extends TableBase { } } +/** + * Data types for attributes within a table + * + * @see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes + */ export enum AttributeType { /** Up to 400KiB of binary data (which must be encoded as base64 before sending to DynamoDB) */ BINARY = 'B', @@ -1133,6 +1159,11 @@ export enum BillingMode { PROVISIONED = 'PROVISIONED', } +/** + * The set of attributes that are projected into the index + * + * @see https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Projection.html + */ export enum ProjectionType { /** Only the index and primary keys are projected into the index. */ KEYS_ONLY = 'KEYS_ONLY', diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 5e84d04c1dc25..6fa3ddbc68189 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -99,21 +99,6 @@ "node": ">= 10.12.0" }, "stability": "stable", - "awslint": { - "exclude": [ - "docs-public-apis:@aws-cdk/aws-dynamodb.TableProps", - "docs-public-apis:@aws-cdk/aws-dynamodb.Table.tableName", - "docs-public-apis:@aws-cdk/aws-dynamodb.Table.tableStreamArn", - "docs-public-apis:@aws-cdk/aws-dynamodb.Attribute", - "docs-public-apis:@aws-cdk/aws-dynamodb.GlobalSecondaryIndexProps", - "docs-public-apis:@aws-cdk/aws-dynamodb.LocalSecondaryIndexProps", - "docs-public-apis:@aws-cdk/aws-dynamodb.SecondaryIndexProps", - "docs-public-apis:@aws-cdk/aws-dynamodb.TableOptions", - "docs-public-apis:@aws-cdk/aws-dynamodb.Table.tableArn", - "docs-public-apis:@aws-cdk/aws-dynamodb.AttributeType", - "docs-public-apis:@aws-cdk/aws-dynamodb.ProjectionType" - ] - }, "awscdkio": { "announce": false }, diff --git a/packages/@aws-cdk/aws-logs/README.md b/packages/@aws-cdk/aws-logs/README.md index a38a753ba9698..478e981010d63 100644 --- a/packages/@aws-cdk/aws-logs/README.md +++ b/packages/@aws-cdk/aws-logs/README.md @@ -216,3 +216,9 @@ const pattern = FilterPattern.spaceDelimited('time', 'component', '...', 'result .whereString('component', '=', 'HttpServer') .whereNumber('result_code', '!=', 200); ``` + +### Notes + +Be aware that Log Group ARNs will always have the string `:*` appended to +them, to match the behavior of [the CloudFormation `AWS::Logs::LogGroup` +resource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#aws-resource-logs-loggroup-return-values). \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 0c5d512348be3..70512828aea3b 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -9,7 +9,8 @@ import { ILogSubscriptionDestination, SubscriptionFilter } from './subscription- export interface ILogGroup extends IResource { /** - * The ARN of this log group + * The ARN of this log group, with ':*' appended + * * @attribute */ readonly logGroupArn: string; @@ -76,7 +77,7 @@ export interface ILogGroup extends IResource { */ abstract class LogGroupBase extends Resource implements ILogGroup { /** - * The ARN of this log group + * The ARN of this log group, with ':*' appended */ public abstract readonly logGroupArn: string; @@ -308,9 +309,11 @@ export class LogGroup extends LogGroupBase { * Import an existing LogGroup given its ARN */ public static fromLogGroupArn(scope: Construct, id: string, logGroupArn: string): ILogGroup { + const baseLogGroupArn = logGroupArn.replace(/:\*$/, ''); + class Import extends LogGroupBase { - public readonly logGroupArn = logGroupArn; - public readonly logGroupName = Stack.of(scope).parseArn(logGroupArn, ':').resourceName!; + public readonly logGroupArn = `${baseLogGroupArn}:*`; + public readonly logGroupName = Stack.of(scope).parseArn(baseLogGroupArn, ':').resourceName!; } return new Import(scope, id); @@ -320,13 +323,15 @@ export class LogGroup extends LogGroupBase { * Import an existing LogGroup given its name */ public static fromLogGroupName(scope: Construct, id: string, logGroupName: string): ILogGroup { + const baseLogGroupName = logGroupName.replace(/:\*$/, ''); + class Import extends LogGroupBase { - public readonly logGroupName = logGroupName; + public readonly logGroupName = baseLogGroupName; public readonly logGroupArn = Stack.of(scope).formatArn({ service: 'logs', resource: 'log-group', sep: ':', - resourceName: logGroupName, + resourceName: baseLogGroupName + ':*', }); } diff --git a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts index e700ef3056c73..ac88fb9c35a7f 100644 --- a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts +++ b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts @@ -140,7 +140,7 @@ export = { // THEN test.deepEqual(imported.logGroupName, 'my-log-group'); - test.deepEqual(imported.logGroupArn, 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group'); + test.deepEqual(imported.logGroupArn, 'arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*'); expect(stack2).to(haveResource('AWS::Logs::LogStream', { LogGroupName: 'my-log-group', })); @@ -157,7 +157,7 @@ export = { // THEN test.deepEqual(imported.logGroupName, 'my-log-group'); - test.ok(/^arn:.+:logs:.+:.+:log-group:my-log-group$/.test(imported.logGroupArn), + test.ok(/^arn:.+:logs:.+:.+:log-group:my-log-group:\*$/.test(imported.logGroupArn), `LogGroup ARN ${imported.logGroupArn} does not match the expected pattern`); expect(stack).to(haveResource('AWS::Logs::LogStream', { LogGroupName: 'my-log-group', @@ -165,6 +165,80 @@ export = { test.done(); }, + 'loggroups imported by name have stream wildcard appended to grant ARN': dataDrivenTests([ + // Regardless of whether the user put :* there already because of this bug, we + // don't want to append it twice. + [''], + [':*'], + ], (test: Test, suffix: string) => { + // GIVEN + const stack = new Stack(); + const user = new iam.User(stack, 'Role'); + const imported = LogGroup.fromLogGroupName(stack, 'lg', `my-log-group${suffix}`); + + // WHEN + imported.grantWrite(user); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ '', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':logs:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':log-group:my-log-group:*', + ]], + }, + }, + ], + }, + })); + test.equal(imported.logGroupName, 'my-log-group'); + + test.done(); + }), + + 'loggroups imported by ARN have stream wildcard appended to grant ARN': dataDrivenTests([ + // Regardless of whether the user put :* there already because of this bug, we + // don't want to append it twice. + [''], + [':*'], + ], (test: Test, suffix: string) => { + // GIVEN + const stack = new Stack(); + const user = new iam.User(stack, 'Role'); + const imported = LogGroup.fromLogGroupArn(stack, 'lg', `arn:aws:logs:us-west-1:123456789012:log-group:my-log-group${suffix}`); + + // WHEN + imported.grantWrite(user); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], + Effect: 'Allow', + Resource: 'arn:aws:logs:us-west-1:123456789012:log-group:my-log-group:*', + }, + ], + }, + })); + test.equal(imported.logGroupName, 'my-log-group'); + + test.done(); + }), + 'extractMetric'(test: Test) { // GIVEN const stack = new Stack(); @@ -242,3 +316,14 @@ export = { test.done(); }, }; + +function dataDrivenTests(cases: any[][], body: (test: Test, ...args: any[]) => void) { + const ret: any = {}; + for (let i = 0; i < cases.length; i++) { + const args = cases[i]; // Need to capture inside loop for safe use inside closure. + ret[`case ${i + 1}`] = function(test: Test) { + return body.apply(this, [test, ...args]); + }; + } + return ret; +} \ No newline at end of file diff --git a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts index 581d2aeb53f3e..fe53d019943fb 100644 --- a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts +++ b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts @@ -229,6 +229,8 @@ async function main() { '*.tsbuildinfo', '', 'tsconfig.json', + '', + '.eslintrc.js', ]); await write('lib/index.ts', [ @@ -264,7 +266,7 @@ async function main() { '```', ]); - await write('.eslintrc.json', [ + await write('.eslintrc.js', [ "const baseConfig = require('../../../tools/cdk-build-tools/config/eslintrc');", 'module.exports = baseConfig;', ]); diff --git a/packages/aws-cdk/test/integ/run-against-dist b/packages/aws-cdk/test/integ/run-against-dist index d10ef39298b1d..eebf758ec2823 100755 --- a/packages/aws-cdk/test/integ/run-against-dist +++ b/packages/aws-cdk/test/integ/run-against-dist @@ -23,13 +23,15 @@ if [[ ! -f $dist_root/build.json ]]; then exit 1 fi +local_cli_version="$(node -e "console.log(require('${dist_root}/build.json').version)")" + serve_npm_packages # Install the CLI and put it on the path -(cd $npmws && npm install aws-cdk) +(cd $npmws && npm install aws-cdk@${local_cli_version}) export PATH=$npmws/node_modules/.bin:$PATH -verify_installed_cli_version +verify_installed_cli_version ${local_cli_version} prepare_java_packages prepare_nuget_packages prepare_python_packages diff --git a/packages/aws-cdk/test/integ/run-against-dist.bash b/packages/aws-cdk/test/integ/run-against-dist.bash index 185365e13f53d..f20c3d65a8676 100644 --- a/packages/aws-cdk/test/integ/run-against-dist.bash +++ b/packages/aws-cdk/test/integ/run-against-dist.bash @@ -80,7 +80,9 @@ function serve_npm_packages() { # Make sure that installed CLI matches the build version function verify_installed_cli_version() { - local expected_version="$(node -e "console.log(require('${dist_root}/build.json').version)")" + + expected_version=$1 + header "Expected CDK version: ${expected_version}" log "Found CDK: $(type -p cdk)"