Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(servicecatalog): allow associating TagOptions with a Portfolio #15612

Merged
merged 8 commits into from
Jul 19, 2021

Conversation

wanjacki
Copy link
Contributor

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

@gitpod-io
Copy link

gitpod-io bot commented Jul 16, 2021

@wanjacki wanjacki changed the title Tag options feat(servicecatalog): Add TagOptions for portfolio Jul 16, 2021
Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but the API needs some polish before we can merge it in.

@@ -157,6 +158,21 @@ A product can be added to multiple portfolios depending on your resource and org
portfolio.addProduct(product);
```

### TagOptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Let's also allow setting the TagOptions directly when creating the Portfolio. So, add an optional readonly tagOptions?: TagOptions property to PortfolioProps.
  2. 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.

@@ -48,6 +49,19 @@ export class AssociationManager {
}
}

public static associateTagOptions(portfolio: IPortfolio, resourceId: string, tagOptions: TagOptions) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I don't think resourceId as a separate parameter makes too much sense. I think you should remove it, and use portfolio.portfolioId in the expression below in CfnTagOptionAssociation.
  2. Specify the return type of this function explicitly please.

@@ -0,0 +1,35 @@
import * as cdk from '@aws-cdk/core';
import { CfnTagOption } from '.';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a weird notation - I wouldn't use it.

@@ -0,0 +1,35 @@
import * as cdk from '@aws-cdk/core';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be called tag-options.ts.

/**
* List of CfnTagOption
*/
public readonly cfnTagOptions: CfnTagOption[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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[]}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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', () => {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this empty line.

@mergify mergify bot dismissed skinny85’s stale review July 17, 2021 00:07

Pull request has been modified.

@wanjacki wanjacki requested a review from skinny85 July 17, 2021 00:16
Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @wanjacki! A few last polishes before we merge this in.

Comment on lines 170 to 171
key1: ['value1', 'value2'],
key2: ['value1'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var -> let

/**
* List of CfnTagOption
*/
public readonly tagOptionsMap: {[key: string]: string[]};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename this variable to portfolioStack.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like I wrote before, I think this should save a defensive copy of the passed map:

Suggested change
this.tagOptionsMap = tagOptionsMap;
this.tagOptionsMap = { ...tagOptionsMap };

So that mutating the tagOptionsMap after creating TagOptions does not cause any changes in the instance.

@@ -48,6 +49,34 @@ export class AssociationManager {
}
}

public static associateTagOptions(portfolio: IPortfolio, tagOptions: TagOptions): void {
Object.keys(tagOptions.tagOptionsMap).forEach(key => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use Object.entries with for...of instead, like this, which will save you accessing the value separately with tagOptions.tagOptionsMap[key] below.

@mergify mergify bot dismissed skinny85’s stale review July 19, 2021 21:38

Pull request has been modified.

@wanjacki wanjacki requested a review from skinny85 July 19, 2021 21:39
@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject89A8053A-LhjRyN9kxr8o
  • Commit ID: c59f455
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @wanjacki!

@mergify mergify bot merged commit e7760ee into aws:master Jul 19, 2021
@mergify
Copy link
Contributor

mergify bot commented Jul 19, 2021

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).

@skinny85 skinny85 changed the title feat(servicecatalog): Add TagOptions for portfolio feat(servicecatalog): allow associating TagOptions with a Portfolio Jul 19, 2021
TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this pull request Aug 3, 2021
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*
hollanddd pushed a commit to hollanddd/aws-cdk that referenced this pull request Aug 26, 2021
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*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants