feat(servicecatalog): allow associating TagOptions with a Portfolio#15612
feat(servicecatalog): allow associating TagOptions with a Portfolio#15612mergify[bot] merged 8 commits intoaws:masterfrom
Conversation
skinny85
left a comment
There was a problem hiding this comment.
Looks good, but the API needs some polish before we can merge it in.
| portfolio.addProduct(product); | ||
| ``` | ||
|
|
||
| ### TagOptions |
There was a problem hiding this comment.
| ### TagOptions | |
| ### Tag options |
| TagOptions allow administrators to easily manage tags on provisioned products by creating a selection of tags for end users to choose from. | ||
| For example, an end user can choose an `ec2` for the instance type size. | ||
| TagOptions are created by specifying a key with a selection of values. | ||
| At the moment, TagOptions can only be disabled on the console. |
There was a problem hiding this comment.
| At the moment, TagOptions can only be disabled on the console. | |
| At the moment, TagOptions can only be disabled in the console. |
| * A TagOption is a key-value pair managed in AWS Service Catalog. | ||
| * It is not an AWS tag, but serves as a template for creating an AWS tag based on the TagOption. | ||
| */ | ||
| addTagOptions(tagOptions: TagOptions): void; |
There was a problem hiding this comment.
- Let's also allow setting the
TagOptionsdirectly when creating thePortfolio. So, add an optionalreadonly tagOptions?: TagOptionsproperty toPortfolioProps. - Since you're using "associate" in the description already, how about we call this method
associateTagOptions()? I'm worried we're overloading the term "add" in this class to mean very many different things.
| } | ||
| } | ||
|
|
||
| public static associateTagOptions(portfolio: IPortfolio, resourceId: string, tagOptions: TagOptions) { |
There was a problem hiding this comment.
- I don't think
resourceIdas a separate parameter makes too much sense. I think you should remove it, and useportfolio.portfolioIdin the expression below inCfnTagOptionAssociation. - Specify the return type of this function explicitly please.
| @@ -0,0 +1,35 @@ | |||
| import * as cdk from '@aws-cdk/core'; | |||
| import { CfnTagOption } from '.'; | |||
There was a problem hiding this comment.
This is a weird notation - I wouldn't use it.
| @@ -0,0 +1,35 @@ | |||
| import * as cdk from '@aws-cdk/core'; | |||
There was a problem hiding this comment.
This file should be called tag-options.ts.
| /** | ||
| * List of CfnTagOption | ||
| */ | ||
| public readonly cfnTagOptions: CfnTagOption[]; |
There was a problem hiding this comment.
We don't use properties from the L1 layer in publicly-available classes like this.
Rename the property to tagOptions, and make it return a {[key: string]: string[]} (make sure it's a defensive copy though, so none can mutate them in that way). Then, move all of the logic of creating CfnTagOptions into the associateTagOptions() method of AssociationManager.
There was a problem hiding this comment.
The reason why the logic is here is because tagOptions are global. Moving it to associateTagOptions would require us to create the L1 when an association is made and do an additional check each time to make sure no duplicate tagOptions are being created. This brings up the issue of what happens when we create one tagOption and apply it to two portfolio, it would create two tagOptions (unless we maintain a map of tagOptions as using tryFindChild won't work due to the different hash/portfolio).
What do you recommend Adam?
There was a problem hiding this comment.
You can still make them global by retrieving the Stack a given Portfolio belongs to with the Stack.of(portfolio) method.
| */ | ||
| public readonly cfnTagOptions: CfnTagOption[]; | ||
|
|
||
| constructor(stack : cdk.Stack, tagOptionsMap: {[key: string]: string[]}) { |
There was a problem hiding this comment.
This is not really an API we would consider adding to the CDK - it's very unnatural.
Get rid of the stack parameter from here.
| }), | ||
|
|
||
| test('fails to add tag options with invalid value length', () => { | ||
|
|
There was a problem hiding this comment.
No need for this empty line.
| key1: ['value1', 'value2'], | ||
| key2: ['value1'], |
There was a problem hiding this comment.
Can we give a more realistic example here? Perhaps the one using the EC2 instance sizes mentioned above?
| const stack = cdk.Stack.of(portfolio); | ||
| const tagOptionKey = hashValues(key, value, stack.node.addr); | ||
| const tagOptionConstructId = `TagOption${tagOptionKey}`; | ||
| var cfnTagOption = stack.node.tryFindChild(tagOptionConstructId) as CfnTagOption; |
| /** | ||
| * List of CfnTagOption | ||
| */ | ||
| public readonly tagOptionsMap: {[key: string]: string[]}; |
There was a problem hiding this comment.
| public readonly tagOptionsMap: {[key: string]: string[]}; | |
| public readonly tagOptionsMap: { [key: string]: string[] }; |
| InputValidator.validateLength(portfolio.node.addr, 'TagOption key', 1, 128, key); | ||
| tagOptions.tagOptionsMap[key].forEach((value: string) => { | ||
| InputValidator.validateLength(portfolio.node.addr, 'TagOption value', 1, 256, value); | ||
| const stack = cdk.Stack.of(portfolio); |
There was a problem hiding this comment.
Let's rename this variable to portfolioStack.
There was a problem hiding this comment.
Also, let's move this out of the loop (it should be done once per associateTagOptions() call, not on each loop iteration).
| public readonly tagOptionsMap: {[key: string]: string[]}; | ||
|
|
||
| constructor(tagOptionsMap: {[key: string]: string[]}) { | ||
| this.tagOptionsMap = tagOptionsMap; |
There was a problem hiding this comment.
Like I wrote before, I think this should save a defensive copy of the passed map:
| this.tagOptionsMap = tagOptionsMap; | |
| this.tagOptionsMap = { ...tagOptionsMap }; |
So that mutating the tagOptionsMap after creating TagOptions does not cause any changes in the instance.
| } | ||
|
|
||
| public static associateTagOptions(portfolio: IPortfolio, tagOptions: TagOptions): void { | ||
| Object.keys(tagOptions.tagOptionsMap).forEach(key => { |
There was a problem hiding this comment.
You can use Object.entries with for...of instead, like this, which will save you accessing the value separately with tagOptions.tagOptionsMap[key] below.
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
|
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
Allows users to add TagOptions to their portfolio. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Allows users to add TagOptions to their portfolio. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Allows users to add TagOptions to their portfolio.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license