-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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(core): warn if an aspect was added via another aspect #8639
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a unit tests
@eladb Before I add a test, I just want to make sure we are good with the current behavior since the Prior to the change, if AspectB was added via AspectA (meaning when AspectA
After the change AspectB will not be invoked on the root or the child constructs. |
I am okay with detecting this and issuing a warning. |
// the `nestedAspectWarning` flag is used to prevent the warning from being emitted for every child | ||
if (!nestedAspectWarning && nodeAspectsCount !== node._aspects.length) { | ||
// tslint:disable-next-line: max-line-length | ||
construct.node.addWarning('We detected an aspect was added via another aspect, this is not supported and may result in an unexpected behavior'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can be more specific than the vague "may result in unexpected behavior."
How about something like?
Aspects added by other Aspects will not be applied; add all Aspects before 'synth' is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we wanted to go with restoring the original behavior, it would look something like this:
const appliedHere = new Set<IAspect>();
let allAspectsHere: IAspect[];
while (true) {
allAspectsHere = [...inheritedAspects ?? [], ...node._aspects];
const yetToApply = allAspectsHere.filter(x => !appliedHere.has(x));
if (yetToApply.length === 0) { break; }
for (const aspect of yetToApply) {
// ...
appliedHere.add(aspect);
}
}
recurse(child, allAspectsHere);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No objections
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
? Any updates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any updates?
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). |
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). |
### Updated PR Since we introduced [`stages`](#8423) which had the unintended side effect of the CDK not supporting adding an Aspect via another Aspect, we have not seen any issues reported beside #8536, that was resolved without requiring this capability. Given that, and the fact this features has many sharp edges we decided to leave it unsupported, and add a warning in the meantime. ----------- Following up on #8536 If an aspect is added via another aspect, the inner aspect will not be invoked. Take for example this code: ```typescript const app = new cdk.App(); app.node.applyAspect({ visit(construct: cdk.IConstruct) { construct.node.applyAspect({ visit(construct: cdk.IConstruct) { console.info("Invoking aspect on construct: " + construct.node.id); // This will not be called } }) } }); ``` Since aspects are added only on the top level node, if an aspect is added while `InvokeAspects` is called on that node, it will be ignored since it will not be added to list of aspects to invoke (`allAspectsHere` in the bellow code): ```typescript function invokeAspects(root: IConstruct) { recurse(root, []); function recurse(construct: IConstruct, inheritedAspects: constructs.IAspect[]) { // hackery to be able to access some private members with strong types (yack!) const node: NodeWithAspectPrivatesHangingOut = construct.node._actualNode as any; const allAspectsHere = [...inheritedAspects ?? [], ...node._aspects]; for (const aspect of allAspectsHere) { if (node.invokedAspects.includes(aspect)) { continue; } aspect.visit(construct); <-- an aspect that was added here will not be added to `allAspectsHere` and will be ignored node.invokedAspects.push(aspect); } for (const child of construct.node.children) { if (!Stage.isStage(child)) { recurse(child, allAspectsHere); } } } } ``` Assuming this is not something we want to support**, we can detect it by comparing the size of `node._aspects` before and after the call to `aspect.visit`, and emit a warning if there has been a change. Note that while the aspect will not be invoked it will be added to every child construct during the recursive visit. Emitting a warning for each child construct will result in a noisy console, to prevent this I have added a flag that will only allow adding **one warning per application**, given this limitation I'm not sure there is a lot of value in adding the warning, thoughts? If we decide to add it I will add tests. (** theoretically we could support it by adding the aspects to `allAspectsHere` during the loop, but this will require a non trivial implementation in order to avoid infinite recursion) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
### Updated PR Since we introduced [`stages`](aws#8423) which had the unintended side effect of the CDK not supporting adding an Aspect via another Aspect, we have not seen any issues reported beside aws#8536, that was resolved without requiring this capability. Given that, and the fact this features has many sharp edges we decided to leave it unsupported, and add a warning in the meantime. ----------- Following up on aws#8536 If an aspect is added via another aspect, the inner aspect will not be invoked. Take for example this code: ```typescript const app = new cdk.App(); app.node.applyAspect({ visit(construct: cdk.IConstruct) { construct.node.applyAspect({ visit(construct: cdk.IConstruct) { console.info("Invoking aspect on construct: " + construct.node.id); // This will not be called } }) } }); ``` Since aspects are added only on the top level node, if an aspect is added while `InvokeAspects` is called on that node, it will be ignored since it will not be added to list of aspects to invoke (`allAspectsHere` in the bellow code): ```typescript function invokeAspects(root: IConstruct) { recurse(root, []); function recurse(construct: IConstruct, inheritedAspects: constructs.IAspect[]) { // hackery to be able to access some private members with strong types (yack!) const node: NodeWithAspectPrivatesHangingOut = construct.node._actualNode as any; const allAspectsHere = [...inheritedAspects ?? [], ...node._aspects]; for (const aspect of allAspectsHere) { if (node.invokedAspects.includes(aspect)) { continue; } aspect.visit(construct); <-- an aspect that was added here will not be added to `allAspectsHere` and will be ignored node.invokedAspects.push(aspect); } for (const child of construct.node.children) { if (!Stage.isStage(child)) { recurse(child, allAspectsHere); } } } } ``` Assuming this is not something we want to support**, we can detect it by comparing the size of `node._aspects` before and after the call to `aspect.visit`, and emit a warning if there has been a change. Note that while the aspect will not be invoked it will be added to every child construct during the recursive visit. Emitting a warning for each child construct will result in a noisy console, to prevent this I have added a flag that will only allow adding **one warning per application**, given this limitation I'm not sure there is a lot of value in adding the warning, thoughts? If we decide to add it I will add tests. (** theoretically we could support it by adding the aspects to `allAspectsHere` during the loop, but this will require a non trivial implementation in order to avoid infinite recursion) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Updated PR
Since we introduced
stages
which had the unintended side effect of the CDK not supporting adding an Aspect via another Aspect, we have not seen any issues reported beside #8536, that was resolved without requiring this capability.Given that, and the fact this features has many sharp edges we decided to leave it unsupported, and add a warning in the meantime.
Following up on #8536
If an aspect is added via another aspect, the inner aspect will not be invoked. Take for example this code:
Since aspects are added only on the top level node, if an aspect is added while
InvokeAspects
is called on that node, it will be ignored since it will not be added to list of aspects to invoke (allAspectsHere
in the bellow code):Assuming this is not something we want to support**, we can detect it by comparing the size of
node._aspects
before and after the call toaspect.visit
, and emit a warning if there has been a change. Note that while the aspect will not be invoked it will be added to every child construct during the recursive visit. Emitting a warning for each child construct will result in a noisy console, to prevent this I have added a flag that will only allow adding one warning per application, given this limitation I'm not sure there is a lot of value in adding the warning, thoughts?If we decide to add it I will add tests.
(** theoretically we could support it by adding the aspects to
allAspectsHere
during the loop, but this will require a non trivial implementation in order to avoid infinite recursion)By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license