From e93bd3e6ef5cdc205bdf8a7458c2931b540a65c4 Mon Sep 17 00:00:00 2001 From: Aidan Crank Date: Tue, 15 Jun 2021 19:45:20 -0400 Subject: [PATCH 1/8] Initial L2 construct for appregisty application Service Catalog AppRegistry application construct initial base version. Please note the ARNS for this construct have two '/' in them hence the slightly different ARN parsing Testing done ------------------ * `yarn build && yarn test` * `yarn integ` Co-authored-by: Dillon Ponzo ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-servicecatalogappregistry/README.md | 36 +++++++- .../lib/application.ts | 90 +++++++++++++++++++ .../lib/index.ts | 2 + .../package.json | 13 +-- .../test/application.test.ts | 71 +++++++++++++++ .../test/integ.application.expected.json | 11 +++ .../test/integ.application.ts | 14 +++ 7 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.expected.json create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md index 06ffc58a41c06..834cf73fea3a2 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md @@ -1,4 +1,4 @@ -# AWS::ServiceCatalogAppRegistry Construct Library +# AWS ServiceCatalogAppRegistry Construct Library --- @@ -9,12 +9,42 @@ > > [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. This means that while you may use them, you may need to update +> your source code when upgrading to a newer version of this package. + --- -This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. +[AWS Service Catalog App Registry](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/appregistry.html) +enables organizations to create and manage repositores of applications and associated resources. + + +```ts +import * as appreg from '@aws-cdk/aws-servicecatalogappregistry' +``` + +## Application + +An AppRegistry application enables you to define your applications and associated resources. +The application name must be unique at the account level, but is mutable. + +```ts +const application = new appreg.Application(this, 'MyFirstApplication', { + applicationName: 'MyFirstApplicationName', + description: 'description for my application', +}); +``` + +An application that has been created outside of the stack can be imported into your CDK app. +Applications can be imported by their ARN via the `Application.fromApplicationArn()` API: ```ts -import servicecatalogappregistry = require('@aws-cdk/aws-servicecatalogappregistry'); +const importedApplication = appreg.Application.fromArn(this, 'MyImportedApplication', +'arn:aws:servicecatalog:us-east-1:012345678910:/applications/0aqmvxvgmry0ecc4mjhwypun6i'); ``` diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts new file mode 100644 index 0000000000000..311fba5a35baf --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts @@ -0,0 +1,90 @@ +import * as cdk from '@aws-cdk/core'; +import { CfnApplication } from './servicecatalogappregistry.generated'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from 'constructs'; + +/** + * A Service Catalog AppRegistry Application. + */ +export interface IApplication extends cdk.IResource { + /** + * The ARN of the application. + * @attribute + */ + readonly applicationArn: string; + + /** + * The ID of the application. + * @attribute + */ + readonly applicationId: string; +} + +/** + * Properties for a Service Catalog AppRegistry Application + */ +export interface ApplicationProps { + /** + * Enforces a particular physical application name. + */ + readonly applicationName: string; + + /** + * Description for application. + * @default - No description provided + */ + readonly description?: string; +} + +abstract class ApplicationBase extends cdk.Resource implements IApplication { + public abstract readonly applicationArn: string; + public abstract readonly applicationId: string; +} + +/** + * A Service Catalog AppRegistry Application. + */ +export class Application extends ApplicationBase implements IApplication { + /** + * Imports an Application construct that represents an external application. + * + * @param scope The parent creating construct (usually `this`). + * @param id The construct's name. + * @param applicationArn the Amazon Resource Name of the existing AppRegistry Application + */ + public static fromApplicationArn(scope: Construct, id: string, applicationArn: string): IApplication { + const applicationId = applicationArn.split('/').pop()!; + + if (applicationArn.split('/').length != 3 || applicationId.length == 0) { + throw new Error('Malformed ARN, cannot determine application ID from: ' + applicationArn); + } + + class Import extends ApplicationBase { + public readonly applicationArn = applicationArn; + public readonly applicationId = applicationId!; + } + + return new Import(scope, id, { + environmentFromArn: applicationArn, + }); + } + + public readonly applicationArn: string; + public readonly applicationId: string; + private readonly application: CfnApplication; + + constructor(scope: Construct, id: string, props: ApplicationProps) { + super(scope, id, { + }); + + this.application = new CfnApplication(this, 'Resource', { + name: props.applicationName, + description: props.description, + }); + + this.applicationId = cdk.Token.asString(this.application.getAtt('Id')); + this.applicationArn = cdk.Token.asString(this.application.getAtt('Arn')); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts index ed6629976cbce..c38ecc6891ef1 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/index.ts @@ -1,2 +1,4 @@ +export * from './application'; + // AWS::ServiceCatalogAppRegistry CloudFormation Resources: export * from './servicecatalogappregistry.generated'; diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/package.json b/packages/@aws-cdk/aws-servicecatalogappregistry/package.json index e47ca12cdb370..e29e277575c45 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/package.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/package.json @@ -77,23 +77,26 @@ }, "license": "Apache-2.0", "devDependencies": { + "@aws-cdk/assert-internal": "0.0.0", "@types/jest": "^26.0.23", "cdk-build-tools": "0.0.0", + "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", - "pkglint": "0.0.0", - "@aws-cdk/assert-internal": "0.0.0" + "pkglint": "0.0.0" }, "dependencies": { - "@aws-cdk/core": "0.0.0" + "@aws-cdk/core": "0.0.0", + "constructs": "^3.3.69" }, "peerDependencies": { - "@aws-cdk/core": "0.0.0" + "@aws-cdk/core": "0.0.0", + "constructs": "^3.3.69" }, "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" }, "stability": "experimental", - "maturity": "cfn-only", + "maturity": "experimental", "awscdkio": { "announce": false }, diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts new file mode 100644 index 0000000000000..f149bc3e507d2 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts @@ -0,0 +1,71 @@ +import '@aws-cdk/assert-internal/jest'; +import * as cdk from '@aws-cdk/core'; +import * as appreg from '../lib'; + +describe('Application', () => { + let stack: cdk.Stack; + + beforeEach(() => { + stack = new cdk.Stack(); + }); + + test('default application creation', () => { + new appreg.Application(stack, 'MyApplication', { + applicationName: 'testApplication', + }); + + expect(stack).toMatchTemplate({ + Resources: { + MyApplication5C63EC1D: { + Type: 'AWS::ServiceCatalogAppRegistry::Application', + Properties: { + Name: 'testApplication', + }, + }, + }, + }); + }), + + test('application with explicit description', () => { + const description = 'my test application description'; + new appreg.Application(stack, 'MyApplication', { + applicationName: 'testApplication', + description: description, + }); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalogAppRegistry::Application', { + Description: description, + }); + }), + + test('application with application tags', () => { + const application = new appreg.Application(stack, 'MyApplication', { + applicationName: 'testApplication', + }); + + cdk.Tags.of(application).add('key1', 'value1'); + cdk.Tags.of(application).add('key2', 'value2'); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalogAppRegistry::Application', { + Tags: { + key1: 'value1', + key2: 'value2', + }, + }); + }), + + test('for an application imported by ARN', () => { + const application = appreg.Application.fromApplicationArn(stack, 'MyApplication', + 'arn:aws:servicecatalog:us-east-1:123456789012:/applications/0aqmvxvgmry0ecc4mjhwypun6i'); + + expect(application.applicationId).toEqual('0aqmvxvgmry0ecc4mjhwypun6i'); + }), + + test('fails for application imported by ARN missing applicationId', () => { + expect(() => { + appreg.Application.fromApplicationArn(stack, 'MyApplication', + 'arn:aws:servicecatalog:us-east-1:123456789012:/applications/'); + }).toThrow(/Malformed ARN, cannot determine application ID from:/); + }); +}); + diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.expected.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.expected.json new file mode 100644 index 0000000000000..9daac86f4dd5c --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.expected.json @@ -0,0 +1,11 @@ +{ + "Resources": { + "TestApplication2FBC585F": { + "Type": "AWS::ServiceCatalogAppRegistry::Application", + "Properties": { + "Name": "myApplicationtest", + "Description": "my application description" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts new file mode 100644 index 0000000000000..969e35727dc30 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts @@ -0,0 +1,14 @@ + +import * as cdk from '@aws-cdk/core'; +import * as appreg from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-servicecatalogappregistry-application'); + +new appreg.Application(stack, 'TestApplication', { + applicationName: 'myApplicationtest', + description: 'my application description', +}); + +app.synth(); + From be2e930e9e414efad16900f18777dfb7b13197ab Mon Sep 17 00:00:00 2001 From: Aidan Crank Date: Wed, 16 Jun 2021 20:02:06 -0400 Subject: [PATCH 2/8] Adding AppRegistry Application construct Addressing comments from PR Co-authored-by: Dillon Ponzo --- .../aws-servicecatalogappregistry/README.md | 4 +-- .../lib/application.ts | 15 +++++--- .../lib/private/validation.ts | 31 +++++++++++++++++ .../test/application.test.ts | 34 ++++++++++++++++++- .../test/integ.application.ts | 1 - 5 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md index 834cf73fea3a2..6773672a8c0c6 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md @@ -37,7 +37,7 @@ The application name must be unique at the account level, but is mutable. ```ts const application = new appreg.Application(this, 'MyFirstApplication', { applicationName: 'MyFirstApplicationName', - description: 'description for my application', + description: 'description for my application', // the description is optional }); ``` @@ -46,5 +46,5 @@ Applications can be imported by their ARN via the `Application.fromApplicationAr ```ts const importedApplication = appreg.Application.fromArn(this, 'MyImportedApplication', -'arn:aws:servicecatalog:us-east-1:012345678910:/applications/0aqmvxvgmry0ecc4mjhwypun6i'); + 'arn:aws:servicecatalog:us-east-1:012345678910:/applications/0aqmvxvgmry0ecc4mjhwypun6i'); ``` diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts index 311fba5a35baf..e9b690a1ad2bd 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts @@ -1,4 +1,5 @@ import * as cdk from '@aws-cdk/core'; +import { InputValidator } from './private/validation'; import { CfnApplication } from './servicecatalogappregistry.generated'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main @@ -56,7 +57,6 @@ export class Application extends ApplicationBase implements IApplication { */ public static fromApplicationArn(scope: Construct, id: string, applicationArn: string): IApplication { const applicationId = applicationArn.split('/').pop()!; - if (applicationArn.split('/').length != 3 || applicationId.length == 0) { throw new Error('Malformed ARN, cannot determine application ID from: ' + applicationArn); } @@ -73,18 +73,23 @@ export class Application extends ApplicationBase implements IApplication { public readonly applicationArn: string; public readonly applicationId: string; - private readonly application: CfnApplication; constructor(scope: Construct, id: string, props: ApplicationProps) { super(scope, id, { }); + this.validateApplicationProps(props); - this.application = new CfnApplication(this, 'Resource', { + const application = new CfnApplication(this, 'Resource', { name: props.applicationName, description: props.description, }); - this.applicationId = cdk.Token.asString(this.application.getAtt('Id')); - this.applicationArn = cdk.Token.asString(this.application.getAtt('Arn')); + this.applicationArn = application.attrArn; + this.applicationId = application.attrId; + } + private validateApplicationProps(props: ApplicationProps) { + InputValidator.validateLength(this.node.path, 'application name', 1, 256, props.applicationName); + InputValidator.validateRegex(this.node.path, 'application name', /^[a-zA-Z0-9-_]+$/, props.applicationName); + InputValidator.validateLength(this.node.path, 'application description', 0, 1024, props.description); } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts new file mode 100644 index 0000000000000..008ad641be4a6 --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts @@ -0,0 +1,31 @@ +/** + * Class to validate that inputs match requirements. + */ +export class InputValidator { + /** + * Validates length is between allowed min and max lengths. + */ + public static validateLength(resourceName: string, inputName: string, minLength: number, maxLength: number, inputString?: string): void { + if (inputString !== undefined && (inputString.length < minLength || inputString.length > maxLength)) { + throw new Error(`Invalid ${inputName} for resource ${resourceName}, must have length between ${minLength} and ${maxLength}, got: '${this.truncateString(inputString, 100)}'`); + } + } + + /** + * Validates a regex. + */ + public static validateRegex(resourceName: string, inputName: string, regex: RegExp, inputString?: string): void { + if (inputString !== undefined && !regex.test(inputString)) { + throw new Error(`Invalid ${inputName} for resource ${resourceName}, must match regex pattern ${regex}, got: '${inputString}'`); + } + } + + private static truncateString(string: string, maxLength: number): string { + if (string.length > maxLength) { + return string.substring(0, maxLength) + '[truncated]'; + } + return string; + } +} + + diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts index f149bc3e507d2..9cdf06695ba14 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts @@ -57,7 +57,6 @@ describe('Application', () => { test('for an application imported by ARN', () => { const application = appreg.Application.fromApplicationArn(stack, 'MyApplication', 'arn:aws:servicecatalog:us-east-1:123456789012:/applications/0aqmvxvgmry0ecc4mjhwypun6i'); - expect(application.applicationId).toEqual('0aqmvxvgmry0ecc4mjhwypun6i'); }), @@ -66,6 +65,39 @@ describe('Application', () => { appreg.Application.fromApplicationArn(stack, 'MyApplication', 'arn:aws:servicecatalog:us-east-1:123456789012:/applications/'); }).toThrow(/Malformed ARN, cannot determine application ID from:/); + }), + + test('fails for application with description length longer than allowed', () => { + expect(() => { + new appreg.Application(stack, 'MyApplication', { + applicationName: 'testApplication', + description: 'too long description'.repeat(1000), + }); + }).toThrow(/Invalid application description for resource/); + }), + + test('fails for application creation with name too short', () => { + expect(() => { + new appreg.Application(stack, 'MyApplication', { + applicationName: '', + }); + }).toThrow(/Invalid application name for resource/); + }), + + test('fails for application with name too long', () => { + expect(() => { + new appreg.Application(stack, 'MyApplication', { + applicationName: 'testApplication'.repeat(50), + }); + }).toThrow(/Invalid application name for resource/); + }), + + test('fails for application with name of invalid characters', () => { + expect(() => { + new appreg.Application(stack, 'MyApplication', { + applicationName: 'My@ppl!iC@ #', + }); + }).toThrow(/Invalid application name for resource/); }); }); diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts index 969e35727dc30..1c3e3cdeb2696 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts @@ -1,4 +1,3 @@ - import * as cdk from '@aws-cdk/core'; import * as appreg from '../lib'; From 517df1b25429a089c0c8d877f1611ddc676a7640 Mon Sep 17 00:00:00 2001 From: Aidan Crank Date: Fri, 18 Jun 2021 15:14:24 -0400 Subject: [PATCH 3/8] Addressing comments from PR Addressing minor formatting comments from PR review. Added functionality to skip validation if we are passed a token for properties, and added unit tests for those cases Testing done ------------------ * `yarn build && yarn lint && yarn test` Co-authored-by: Dillon Ponzo --- .../aws-servicecatalogappregistry/README.md | 2 +- .../lib/application.ts | 7 ++++--- .../lib/private/validation.ts | 5 +++-- .../test/application.test.ts | 19 +++++++++++++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md index 6773672a8c0c6..09a668c026c13 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md @@ -45,6 +45,6 @@ An application that has been created outside of the stack can be imported into y Applications can be imported by their ARN via the `Application.fromApplicationArn()` API: ```ts -const importedApplication = appreg.Application.fromArn(this, 'MyImportedApplication', +const importedApplication = appreg.Application.fromApplicationArn(this, 'MyImportedApplication', 'arn:aws:servicecatalog:us-east-1:012345678910:/applications/0aqmvxvgmry0ecc4mjhwypun6i'); ``` diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts index e9b690a1ad2bd..8c67347fed741 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts @@ -47,7 +47,7 @@ abstract class ApplicationBase extends cdk.Resource implements IApplication { /** * A Service Catalog AppRegistry Application. */ -export class Application extends ApplicationBase implements IApplication { +export class Application extends ApplicationBase { /** * Imports an Application construct that represents an external application. * @@ -75,8 +75,8 @@ export class Application extends ApplicationBase implements IApplication { public readonly applicationId: string; constructor(scope: Construct, id: string, props: ApplicationProps) { - super(scope, id, { - }); + super(scope, id); + this.validateApplicationProps(props); const application = new CfnApplication(this, 'Resource', { @@ -87,6 +87,7 @@ export class Application extends ApplicationBase implements IApplication { this.applicationArn = application.attrArn; this.applicationId = application.attrId; } + private validateApplicationProps(props: ApplicationProps) { InputValidator.validateLength(this.node.path, 'application name', 1, 256, props.applicationName); InputValidator.validateRegex(this.node.path, 'application name', /^[a-zA-Z0-9-_]+$/, props.applicationName); diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts index 008ad641be4a6..4f28d2f1f9c7a 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts @@ -1,3 +1,4 @@ +import * as cdk from '@aws-cdk/core'; /** * Class to validate that inputs match requirements. */ @@ -6,7 +7,7 @@ export class InputValidator { * Validates length is between allowed min and max lengths. */ public static validateLength(resourceName: string, inputName: string, minLength: number, maxLength: number, inputString?: string): void { - if (inputString !== undefined && (inputString.length < minLength || inputString.length > maxLength)) { + if (!cdk.Token.isUnresolved(inputString) && inputString !== undefined && (inputString.length < minLength || inputString.length > maxLength)) { throw new Error(`Invalid ${inputName} for resource ${resourceName}, must have length between ${minLength} and ${maxLength}, got: '${this.truncateString(inputString, 100)}'`); } } @@ -15,7 +16,7 @@ export class InputValidator { * Validates a regex. */ public static validateRegex(resourceName: string, inputName: string, regex: RegExp, inputString?: string): void { - if (inputString !== undefined && !regex.test(inputString)) { + if (!cdk.Token.isUnresolved(inputString) && inputString !== undefined && !regex.test(inputString)) { throw new Error(`Invalid ${inputName} for resource ${resourceName}, must match regex pattern ${regex}, got: '${inputString}'`); } } diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts index 9cdf06695ba14..874a89696c8cd 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts @@ -67,6 +67,25 @@ describe('Application', () => { }).toThrow(/Malformed ARN, cannot determine application ID from:/); }), + test('application created with a token description does not throw validation error', () => { + const tokenDescription = cdk.Lazy.string({ produce: () => 'token description'.repeat(1000) }); + expect(() => { + new appreg.Application(stack, 'MyApplication', { + applicationName: 'myApplication', + description: tokenDescription, + }); + }).not.toThrow(/Invalid application description for resource/); + }), + + test('application created with a token name does not throw validation error', () => { + const tokenName = cdk.Lazy.string({ produce: () => 'token Application Name' }); + expect(() => { + new appreg.Application(stack, 'MyApplication', { + applicationName: tokenName, + }); + }).not.toThrow(/Invalid application name for resource/); + }), + test('fails for application with description length longer than allowed', () => { expect(() => { new appreg.Application(stack, 'MyApplication', { From 0ab78e0624fb56c71a316d41766d0d355ef45c79 Mon Sep 17 00:00:00 2001 From: Aidan Crank Date: Mon, 21 Jun 2021 15:51:01 -0400 Subject: [PATCH 4/8] Fixing input validation tests Updating unit tests to properly test that token inputs do end up getting created in template and not just validating it throwing a specific error Testing done ------------------ * `yarn build && yarn lint && yarn test` Co-authored-by Dillon Ponzo --- .../lib/private/validation.ts | 3 +- .../test/application.test.ts | 40 ++++++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts index 4f28d2f1f9c7a..d7cdeeb72819f 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts @@ -1,4 +1,5 @@ import * as cdk from '@aws-cdk/core'; + /** * Class to validate that inputs match requirements. */ @@ -17,7 +18,7 @@ export class InputValidator { */ public static validateRegex(resourceName: string, inputName: string, regex: RegExp, inputString?: string): void { if (!cdk.Token.isUnresolved(inputString) && inputString !== undefined && !regex.test(inputString)) { - throw new Error(`Invalid ${inputName} for resource ${resourceName}, must match regex pattern ${regex}, got: '${inputString}'`); + throw new Error(`Invalid ${inputName} for resource ${resourceName}, must match regex pattern ${regex}, got: '${this.truncateString(inputString, 100)}'`); } } diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts index 874a89696c8cd..53aebf925df38 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts @@ -67,23 +67,33 @@ describe('Application', () => { }).toThrow(/Malformed ARN, cannot determine application ID from:/); }), - test('application created with a token description does not throw validation error', () => { - const tokenDescription = cdk.Lazy.string({ produce: () => 'token description'.repeat(1000) }); - expect(() => { - new appreg.Application(stack, 'MyApplication', { - applicationName: 'myApplication', - description: tokenDescription, - }); - }).not.toThrow(/Invalid application description for resource/); + test('application created with a token description does not throw validation error and creates', () => { + const tokenDescription = new cdk.CfnParameter(stack, 'Description'); + + new appreg.Application(stack, 'MyApplication', { + applicationName: 'myApplication', + description: tokenDescription.valueAsString, + }); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalogAppRegistry::Application', { + Description: { + Ref: 'Description', + }, + }); }), - test('application created with a token name does not throw validation error', () => { - const tokenName = cdk.Lazy.string({ produce: () => 'token Application Name' }); - expect(() => { - new appreg.Application(stack, 'MyApplication', { - applicationName: tokenName, - }); - }).not.toThrow(/Invalid application name for resource/); + test('application created with a token application name does not throw validation error', () => { + const tokenApplicationName= new cdk.CfnParameter(stack, 'ApplicationName'); + + new appreg.Application(stack, 'MyApplication', { + applicationName: tokenApplicationName.valueAsString, + }); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalogAppRegistry::Application', { + Name: { + Ref: 'ApplicationName', + }, + }); }), test('fails for application with description length longer than allowed', () => { From 1ba5eed9e7f1ae185cb819d9616a4b14e94bf797 Mon Sep 17 00:00:00 2001 From: Aidan Crank Date: Mon, 21 Jun 2021 15:57:35 -0400 Subject: [PATCH 5/8] removing trailing whitespaces... --- .../aws-servicecatalogappregistry/lib/private/validation.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts index d7cdeeb72819f..efee933061c3f 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/private/validation.ts @@ -28,6 +28,4 @@ export class InputValidator { } return string; } -} - - +} \ No newline at end of file From c6521297cfa6559737ccd5e1b29cb05acfabf04c Mon Sep 17 00:00:00 2001 From: arcrank Date: Mon, 21 Jun 2021 18:25:28 -0400 Subject: [PATCH 6/8] Update packages/@aws-cdk/aws-servicecatalogappregistry/README.md Co-authored-by: Adam Ruka --- packages/@aws-cdk/aws-servicecatalogappregistry/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md index 09a668c026c13..457121401a258 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/README.md +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/README.md @@ -26,7 +26,7 @@ enables organizations to create and manage repositores of applications and assoc ```ts -import * as appreg from '@aws-cdk/aws-servicecatalogappregistry' +import * as appreg from '@aws-cdk/aws-servicecatalogappregistry'; ``` ## Application From be8b38b7d26f50d2078c2bcabf5bd4e839f93a3e Mon Sep 17 00:00:00 2001 From: arcrank Date: Mon, 21 Jun 2021 18:25:50 -0400 Subject: [PATCH 7/8] Update packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts Co-authored-by: Adam Ruka --- .../aws-servicecatalogappregistry/lib/application.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts index 8c67347fed741..21416ddd5b1b0 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts @@ -56,7 +56,8 @@ export class Application extends ApplicationBase { * @param applicationArn the Amazon Resource Name of the existing AppRegistry Application */ public static fromApplicationArn(scope: Construct, id: string, applicationArn: string): IApplication { - const applicationId = applicationArn.split('/').pop()!; + const arn = cdk.Stack.of(scope).splitArn(applicationArn, cdk.ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME); + const applicationId = arn.resourceName; if (applicationArn.split('/').length != 3 || applicationId.length == 0) { throw new Error('Malformed ARN, cannot determine application ID from: ' + applicationArn); } @@ -93,4 +94,4 @@ export class Application extends ApplicationBase { InputValidator.validateRegex(this.node.path, 'application name', /^[a-zA-Z0-9-_]+$/, props.applicationName); InputValidator.validateLength(this.node.path, 'application description', 0, 1024, props.description); } -} \ No newline at end of file +} From bbfb0f11094ed54e973b03a625f6aa864b20e08c Mon Sep 17 00:00:00 2001 From: Aidan Crank Date: Mon, 21 Jun 2021 18:35:52 -0400 Subject: [PATCH 8/8] updating ARN parsing and error messaging --- .../aws-servicecatalogappregistry/lib/application.ts | 5 +++-- .../aws-servicecatalogappregistry/test/application.test.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts index 21416ddd5b1b0..3f93a10671c82 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts @@ -58,8 +58,9 @@ export class Application extends ApplicationBase { public static fromApplicationArn(scope: Construct, id: string, applicationArn: string): IApplication { const arn = cdk.Stack.of(scope).splitArn(applicationArn, cdk.ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME); const applicationId = arn.resourceName; - if (applicationArn.split('/').length != 3 || applicationId.length == 0) { - throw new Error('Malformed ARN, cannot determine application ID from: ' + applicationArn); + + if (!applicationId) { + throw new Error('Missing required Application ID from Application ARN: ' + applicationArn); } class Import extends ApplicationBase { diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts index 53aebf925df38..6dc3c9e823105 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts @@ -64,7 +64,7 @@ describe('Application', () => { expect(() => { appreg.Application.fromApplicationArn(stack, 'MyApplication', 'arn:aws:servicecatalog:us-east-1:123456789012:/applications/'); - }).toThrow(/Malformed ARN, cannot determine application ID from:/); + }).toThrow(/Missing required Application ID from Application ARN:/); }), test('application created with a token description does not throw validation error and creates', () => {