Skip to content

Commit

Permalink
fix(aspects): "localAspects is not iterable" error (#32647)
Browse files Browse the repository at this point in the history
Closes #32470 

### Reason for this change

Some customers have reported seeing the error `TypeError: localAspects is not iterable` upon upgrading to CDK v2.172.0 (this is when the Priority-ordered aspects feature was released). This is likely caused by customers having dependencies on third-party constructs/libraries which are using outdated versions (< 2.172.0) of CDK. The problem more specifically is that the `Aspects.applied` function was added in v2.172.0, and the new `invokeAspects` function calls this function on all nodes in the tree.

### Description of changes

Created a workaround for customers. Added the `getAspectApplications` function in `synthesis.ts` - this function creates `AspectApplication` objects from `Aspects.all` if `Aspects.applied` does not exist.

### Describe any new or updated permissions being added

None.

### Description of how you validated changes

New unit test in `aspect.test.ts` with a monkey patched `Aspects.applied` function.

### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
sumupitchayan authored Dec 23, 2024
1 parent 0150854 commit cb985ba
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
22 changes: 18 additions & 4 deletions packages/aws-cdk-lib/core/lib/private/synthesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CloudAssembly } from '../../../cx-api';
import * as cxapi from '../../../cx-api';
import { Annotations } from '../annotations';
import { App } from '../app';
import { AspectApplication, Aspects } from '../aspect';
import { AspectApplication, AspectPriority, Aspects } from '../aspect';
import { FileSystem } from '../fs';
import { Stack } from '../stack';
import { ISynthesisSession } from '../stack-synthesizers/types';
Expand Down Expand Up @@ -228,7 +228,7 @@ function invokeAspects(root: IConstruct) {
const node = construct.node;
const aspects = Aspects.of(construct);

let localAspects = aspects.applied;
let localAspects = getAspectApplications(construct);
const allAspectsHere = sortAspectsByPriority(inheritedAspects, localAspects);

const nodeAspectsCount = aspects.all.length;
Expand Down Expand Up @@ -290,11 +290,10 @@ function invokeAspectsV2(root: IConstruct) {

function recurse(construct: IConstruct, inheritedAspects: AspectApplication[]): boolean {
const node = construct.node;
const aspects = Aspects.of(construct);

let didSomething = false;

let localAspects = aspects.applied;
let localAspects = getAspectApplications(construct);
const allAspectsHere = sortAspectsByPriority(inheritedAspects, localAspects);

for (const aspectApplication of allAspectsHere) {
Expand Down Expand Up @@ -354,6 +353,21 @@ function sortAspectsByPriority(inheritedAspects: AspectApplication[], localAspec
return allAspects;
}

/**
* Helper function to get aspect applications.
* If `Aspects.applied` is available, it is used; otherwise, create AspectApplications from `Aspects.all`.
*/
function getAspectApplications(node: IConstruct): AspectApplication[] {
const aspects = Aspects.of(node);
if (aspects.applied !== undefined) {
return aspects.applied;
}

// Fallback: Create AspectApplications from `aspects.all`
const typedAspects = aspects as Aspects;
return typedAspects.all.map(aspect => new AspectApplication(node, aspect, AspectPriority.DEFAULT));
}

/**
* Find all stacks and add Metadata Resources to all of them
*
Expand Down
20 changes: 20 additions & 0 deletions packages/aws-cdk-lib/core/test/aspect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,24 @@ describe('aspect', () => {
}
}
}

test.each([
{ stabilization: true },
{ stabilization: false },
])('Error is not thrown if Aspects.applied does not exist (stabilization: $stabilization)', ({ stabilization }) => {
const app = new App({ context: { '@aws-cdk/core:aspectStabilization': stabilization } });
const root = new Stack(app, 'My-Stack');

Aspects.of(root).add(new Tag('AspectA', 'Visited'));

// "Monkey patching" - Override `applied` to simulate its absence
Object.defineProperty(Aspects.prototype, 'applied', {
value: undefined,
configurable: true,
});

expect(() => {
app.synth();
}).not.toThrow();
});
});

0 comments on commit cb985ba

Please sign in to comment.