From 70d7a4cd2337e8fcc4292e035783934d2a357070 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 9 Mar 2025 03:44:47 +0900 Subject: [PATCH 01/19] feat(cli): can ignore errors and return dummy value in cc api context provider --- .../lib/context-providers/cc-api-provider.ts | 56 ++++-- .../context-providers/cc-api-provider.test.ts | 187 ++++++++++++++++++ 2 files changed, 226 insertions(+), 17 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 607baaee..7c91b082 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -33,10 +33,10 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { if (args.exactIdentifier) { // use getResource to get the exact indentifier - return this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn); + return this.getResource(cc, args); } else { // use listResource - return this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn); + return this.listResources(cc, args); } } @@ -49,26 +49,30 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { */ private async getResource( cc: ICloudControlClient, - typeName: string, - exactIdentifier: string, - propertiesToReturn: string[], + args: CcApiContextQuery, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; try { const result = await cc.getResource({ - TypeName: typeName, - Identifier: exactIdentifier, + TypeName: args.typeName, + Identifier: args.exactIdentifier, }); const id = result.ResourceDescription?.Identifier ?? ''; if (id !== '') { const propsObject = JSON.parse(result.ResourceDescription?.Properties ?? ''); - const propsObj = getResultObj(propsObject, result.ResourceDescription?.Identifier!, propertiesToReturn); + const propsObj = getResultObj(propsObject, result.ResourceDescription?.Identifier!, args.propertiesToReturn); resultObjs.push(propsObj); } else { - throw new ContextProviderError(`Could not get resource ${exactIdentifier}.`); + throw new ContextProviderError(`Could not get resource ${args.exactIdentifier}.`); } } catch (err) { - throw new ContextProviderError(`Encountered CC API error while getting resource ${exactIdentifier}. Error: ${err}`); + const dummyValue = this.tryGetDummyValue(args); + if (dummyValue) { + const propsObj = getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn); + resultObjs.push(propsObj); + return resultObjs; + } + throw new ContextProviderError(`Encountered CC API error while getting resource ${args.exactIdentifier}. Error: ${err}`); } return resultObjs; } @@ -82,22 +86,20 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { */ private async listResources( cc: ICloudControlClient, - typeName: string, - propertyMatch: Record, - propertiesToReturn: string[], + args: CcApiContextQuery, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; try { const result = await cc.listResources({ - TypeName: typeName, + TypeName: args.typeName, }); result.ResourceDescriptions?.forEach((resource) => { const id = resource.Identifier ?? ''; if (id !== '') { const propsObject = JSON.parse(resource.Properties ?? ''); - const filters = Object.entries(propertyMatch); + const filters = Object.entries(args.propertyMatch!); let match = false; if (filters) { match = filters.every((record, _index, _arr) => { @@ -114,14 +116,34 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } if (match) { - const propsObj = getResultObj(propsObject, resource.Identifier!, propertiesToReturn); + const propsObj = getResultObj(propsObject, resource.Identifier!, args.propertiesToReturn); resultObjs.push(propsObj); } } }); } catch (err) { - throw new ContextProviderError(`Could not get resources ${JSON.stringify(propertyMatch)}. Error: ${err}`); + const dummyValue = this.tryGetDummyValue(args); + if (dummyValue) { + const propsObj = getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn); + resultObjs.push(propsObj); + return resultObjs; + } + throw new ContextProviderError(`Could not get resources ${JSON.stringify(args.propertyMatch)}. Error: ${err}`); } return resultObjs; } + + private tryGetDummyValue(args: CcApiContextQuery): Record | undefined { + if (!('ignoreErrorOnMissingContext' in args) || !args.ignoreErrorOnMissingContext) { + return undefined; + } + if (!('dummyValue' in args) || !Array.isArray(args.dummyValue) || args.dummyValue.length === 0) { + return undefined; + } + const dummyValue = args.dummyValue[0]; + if (typeof dummyValue !== 'object' || dummyValue === null) { + return undefined; + } + return dummyValue; + } } diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index ab71efde..925457e2 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -1,6 +1,7 @@ import { GetResourceCommand, ListResourcesCommand } from '@aws-sdk/client-cloudcontrol'; import { CcApiContextProviderPlugin } from '../../lib/context-providers/cc-api-provider'; import { mockCloudControlClient, MockSdkProvider, restoreSdkMocksToDefault } from '../util/mock-sdk'; +import { CcApiContextQuery } from '@aws-cdk/cloud-assembly-schema'; let provider: CcApiContextProviderPlugin; @@ -240,4 +241,190 @@ test('error by specifying neither exactIdentifier or propertyMatch', async () => }), ).rejects.toThrow('Neither exactIdentifier nor propertyMatch is specified. Failed to find resources using CC API for type AWS::RDS::DBInstance.'); // THEN }); + +describe('dummy value', () => { + test('returns dummy value when CC API getResource fails', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + + // WHEN + const results = await provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreErrorOnMissingContext: true, + dummyValue: [ + { + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + }, + ], + } as TestQueryWithDummy); + + // THEN + expect(results.length).toEqual(1); + expect(results[0]).toEqual({ + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + Identifier: 'dummy-id', + }); + }); + + test('returns dummy value when CC API listResources fails', async () => { + // GIVEN + mockCloudControlClient.on(ListResourcesCommand).rejects('No data found'); + + // WHEN + const results = await provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + propertyMatch: { 'StorageEncrypted': 'true' }, + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreErrorOnMissingContext: true, + dummyValue: [ + { + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + }, + ], + } as TestQueryWithDummy); + + // THEN + expect(results.length).toEqual(1); + expect(results[0]).toEqual({ + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + Identifier: 'dummy-id', + }); + }); + + test('throws error when CC API fails and ignoreErrorOnMissingContext is not provided', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + dummyValue: [ + { + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + }, + ], + } as TestQueryWithDummy), + ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + }); + + test('throws error when CC API fails and ignoreErrorOnMissingContext is false', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreErrorOnMissingContext: false, + dummyValue: [ + { + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + }, + ], + } as TestQueryWithDummy), + ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + }); + + test('throws error when CC API fails and dummyValue is not provided', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreErrorOnMissingContext: true, + } as TestQueryWithDummy), + ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + }); + + test('throws error when CC API fails and dummyValue is not an array', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreErrorOnMissingContext: true, + dummyValue: { + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + }, + } as TestQueryWithDummy), + ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + }); + + test('throws error when CC API fails and dummyValue is an empty array', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreErrorOnMissingContext: true, + dummyValue: [], + } as TestQueryWithDummy), + ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + }); + + test('throws error when CC API fails and dummyValue is not an object array', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreErrorOnMissingContext: true, + dummyValue: [ + 'not an object', + ], + } as TestQueryWithDummy), + ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + }); +}); /* eslint-enable */ + +interface TestQueryWithDummy extends CcApiContextQuery { + ignoreErrorOnMissingContext?: boolean; + dummyValue?: any; +} From 08b4b121ee6c544b9a6df95d7f09529c8158b6ac Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 9 Mar 2025 04:23:20 +0900 Subject: [PATCH 02/19] method name --- packages/aws-cdk/lib/context-providers/cc-api-provider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 7c91b082..3421c430 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -66,7 +66,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { throw new ContextProviderError(`Could not get resource ${args.exactIdentifier}.`); } } catch (err) { - const dummyValue = this.tryGetDummyValue(args); + const dummyValue = this.getDummyValueIfErrorIgnored(args); if (dummyValue) { const propsObj = getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn); resultObjs.push(propsObj); @@ -122,7 +122,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } }); } catch (err) { - const dummyValue = this.tryGetDummyValue(args); + const dummyValue = this.getDummyValueIfErrorIgnored(args); if (dummyValue) { const propsObj = getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn); resultObjs.push(propsObj); @@ -133,7 +133,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { return resultObjs; } - private tryGetDummyValue(args: CcApiContextQuery): Record | undefined { + private getDummyValueIfErrorIgnored(args: CcApiContextQuery): Record | undefined { if (!('ignoreErrorOnMissingContext' in args) || !args.ignoreErrorOnMissingContext) { return undefined; } From 2090861bb52cf745025b82ffb9618f3a3132f788 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Wed, 12 Mar 2025 12:41:57 +0900 Subject: [PATCH 03/19] wip --- .../lib/context-providers/cc-api-provider.ts | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 3421c430..55df371e 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -5,6 +5,17 @@ import { ContextProviderPlugin } from '../api/plugin'; import { ContextProviderError } from '../toolkit/error'; import { findJsonValue, getResultObj } from '../util'; +type CcApiContextQueryGet = Required>; +type CcApiContextQueryList = Required>; + +function isGetQuery(x: CcApiContextQuery): x is CcApiContextQueryGet { + return !!x.exactIdentifier; +} + +function isListQuery(x: CcApiContextQuery): x is CcApiContextQueryList { + return !!x.propertyMatch; +} + export class CcApiContextProviderPlugin implements ContextProviderPlugin { constructor(private readonly aws: SdkProvider) { } @@ -24,14 +35,17 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } private async findResources(cc: ICloudControlClient, args: CcApiContextQuery): Promise<{[key: string]: any} []> { - if (args.exactIdentifier && args.propertyMatch) { + const isGet = isGetQuery(args); + const isList = isListQuery(args); + + if (isGet && isList) { throw new ContextProviderError(`Specify either exactIdentifier or propertyMatch, but not both. Failed to find resources using CC API for type ${args.typeName}.`); } - if (!args.exactIdentifier && !args.propertyMatch) { + if (!isGet && !isList) { throw new ContextProviderError(`Neither exactIdentifier nor propertyMatch is specified. Failed to find resources using CC API for type ${args.typeName}.`); } - if (args.exactIdentifier) { + if (isGet) { // use getResource to get the exact indentifier return this.getResource(cc, args); } else { @@ -49,7 +63,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { */ private async getResource( cc: ICloudControlClient, - args: CcApiContextQuery, + args: CcApiContextQueryGet, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; try { @@ -86,7 +100,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { */ private async listResources( cc: ICloudControlClient, - args: CcApiContextQuery, + args: CcApiContextQueryList, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; @@ -99,7 +113,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { if (id !== '') { const propsObject = JSON.parse(resource.Properties ?? ''); - const filters = Object.entries(args.propertyMatch!); + const filters = Object.entries(args.propertyMatch); let match = false; if (filters) { match = filters.every((record, _index, _arr) => { From ffc040ebca2a668c719f873d283b18a7c3f54caf Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:06:47 +0900 Subject: [PATCH 04/19] call getDummyValueIfErrorIgnored in findResources --- .../lib/context-providers/cc-api-provider.ts | 76 ++++++++----------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 55df371e..46b90583 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -5,17 +5,6 @@ import { ContextProviderPlugin } from '../api/plugin'; import { ContextProviderError } from '../toolkit/error'; import { findJsonValue, getResultObj } from '../util'; -type CcApiContextQueryGet = Required>; -type CcApiContextQueryList = Required>; - -function isGetQuery(x: CcApiContextQuery): x is CcApiContextQueryGet { - return !!x.exactIdentifier; -} - -function isListQuery(x: CcApiContextQuery): x is CcApiContextQueryList { - return !!x.propertyMatch; -} - export class CcApiContextProviderPlugin implements ContextProviderPlugin { constructor(private readonly aws: SdkProvider) { } @@ -35,22 +24,27 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } private async findResources(cc: ICloudControlClient, args: CcApiContextQuery): Promise<{[key: string]: any} []> { - const isGet = isGetQuery(args); - const isList = isListQuery(args); - - if (isGet && isList) { + if (args.exactIdentifier && args.propertyMatch) { throw new ContextProviderError(`Specify either exactIdentifier or propertyMatch, but not both. Failed to find resources using CC API for type ${args.typeName}.`); } - if (!isGet && !isList) { + if (!args.exactIdentifier && !args.propertyMatch) { throw new ContextProviderError(`Neither exactIdentifier nor propertyMatch is specified. Failed to find resources using CC API for type ${args.typeName}.`); } - if (isGet) { - // use getResource to get the exact indentifier - return this.getResource(cc, args); - } else { - // use listResource - return this.listResources(cc, args); + try { + if (args.exactIdentifier) { + // use getResource to get the exact indentifier + return await this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn); + } else { + // use listResource + return await this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn); + } + } catch (err) { + const dummyValue = this.getDummyValueIfErrorIgnored(args); + if (dummyValue) { + return [getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn)]; + } + throw err; } } @@ -63,30 +57,26 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { */ private async getResource( cc: ICloudControlClient, - args: CcApiContextQueryGet, + typeName: string, + exactIdentifier: string, + propertiesToReturn: string[], ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; try { const result = await cc.getResource({ - TypeName: args.typeName, - Identifier: args.exactIdentifier, + TypeName: typeName, + Identifier: exactIdentifier, }); const id = result.ResourceDescription?.Identifier ?? ''; if (id !== '') { const propsObject = JSON.parse(result.ResourceDescription?.Properties ?? ''); - const propsObj = getResultObj(propsObject, result.ResourceDescription?.Identifier!, args.propertiesToReturn); + const propsObj = getResultObj(propsObject, result.ResourceDescription?.Identifier!, propertiesToReturn); resultObjs.push(propsObj); } else { - throw new ContextProviderError(`Could not get resource ${args.exactIdentifier}.`); + throw new ContextProviderError(`Could not get resource ${exactIdentifier}.`); } } catch (err) { - const dummyValue = this.getDummyValueIfErrorIgnored(args); - if (dummyValue) { - const propsObj = getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn); - resultObjs.push(propsObj); - return resultObjs; - } - throw new ContextProviderError(`Encountered CC API error while getting resource ${args.exactIdentifier}. Error: ${err}`); + throw new ContextProviderError(`Encountered CC API error while getting resource ${exactIdentifier}. Error: ${err}`); } return resultObjs; } @@ -100,20 +90,22 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { */ private async listResources( cc: ICloudControlClient, - args: CcApiContextQueryList, + typeName: string, + propertyMatch: Record, + propertiesToReturn: string[], ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; try { const result = await cc.listResources({ - TypeName: args.typeName, + TypeName: typeName, }); result.ResourceDescriptions?.forEach((resource) => { const id = resource.Identifier ?? ''; if (id !== '') { const propsObject = JSON.parse(resource.Properties ?? ''); - const filters = Object.entries(args.propertyMatch); + const filters = Object.entries(propertyMatch); let match = false; if (filters) { match = filters.every((record, _index, _arr) => { @@ -130,19 +122,13 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } if (match) { - const propsObj = getResultObj(propsObject, resource.Identifier!, args.propertiesToReturn); + const propsObj = getResultObj(propsObject, resource.Identifier!, propertiesToReturn); resultObjs.push(propsObj); } } }); } catch (err) { - const dummyValue = this.getDummyValueIfErrorIgnored(args); - if (dummyValue) { - const propsObj = getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn); - resultObjs.push(propsObj); - return resultObjs; - } - throw new ContextProviderError(`Could not get resources ${JSON.stringify(args.propertyMatch)}. Error: ${err}`); + throw new ContextProviderError(`Could not get resources ${JSON.stringify(propertyMatch)}. Error: ${err}`); } return resultObjs; } From b69d69be836002a230b4df638193f8f63f867634 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:06:58 +0900 Subject: [PATCH 05/19] fix docs --- .../lib/cloud-assembly/context-queries.ts | 1 - .../cloud-assembly-schema/schema/cloud-assembly.schema.json | 2 +- packages/@aws-cdk/cloud-assembly-schema/schema/version.json | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index 83649c51..756dd78c 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -374,7 +374,6 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { /** * This indicates the property to search for. - * If both exactIdentifier and propertyMatch are specified, then exactIdentifier is used. * Specifying propertyMatch will return 0 or more results. * Either exactIdentifier or propertyMatch should be specified. * @default - None diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 02559f26..c6caef11 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -1036,7 +1036,7 @@ "type": "string" }, "propertyMatch": { - "description": "This indicates the property to search for.\nIf both exactIdentifier and propertyMatch are specified, then exactIdentifier is used.\nSpecifying propertyMatch will return 0 or more results.\nEither exactIdentifier or propertyMatch should be specified. (Default - None)", + "description": "This indicates the property to search for.\nSpecifying propertyMatch will return 0 or more results.\nEither exactIdentifier or propertyMatch should be specified. (Default - None)", "$ref": "#/definitions/Record" }, "propertiesToReturn": { diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 29669cfc..037caff8 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { - "schemaHash": "ba7d47a7a023c39293e99a374af293384eaf1ccd207e515dbdc59dfb5cae4ed6", - "revision": 41 + "schemaHash": "e3dbcdbb69e3f47ccd83dd0bc45ef715dd217df6aa61e296309af2f929ad80fd", + "revision": 42 } \ No newline at end of file From 256059ac11a5c55ab0a3438d21fd0df4c99f3091 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:26:28 +0900 Subject: [PATCH 06/19] change CcApiContextQuery interface and modify tests --- .../lib/cloud-assembly/context-queries.ts | 12 +++++++++++ .../schema/cloud-assembly.schema.json | 8 +++++++ .../cloud-assembly-schema/schema/version.json | 4 ++-- .../lib/context-providers/cc-api-provider.ts | 4 ++-- .../context-providers/cc-api-provider.test.ts | 21 +++++++------------ 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index 756dd78c..33ffac96 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -384,6 +384,18 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { * This is a set of properties returned from CC API that we want to return from ContextQuery. */ readonly propertiesToReturn: string[]; + + /** + * The value to return if the context value was not found. + * @default - None + */ + readonly dummyValue?: any; + + /** + * When True, the context provider don't throw an error and return the dummyValue if the resource was not found. + * @default false + */ + readonly ignoreErrorOnMissingContext?: boolean; } /** diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index c6caef11..e73d0294 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -1046,6 +1046,14 @@ "type": "string" } }, + "dummyValue": { + "description": "The value to return if the context value was not found. (Default - None)" + }, + "ignoreErrorOnMissingContext": { + "description": "When True, the context provider don't throw an error and return the dummyValue if the resource was not found.", + "default": false, + "type": "boolean" + }, "account": { "description": "Query account", "type": "string" diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 037caff8..a65b95de 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { - "schemaHash": "e3dbcdbb69e3f47ccd83dd0bc45ef715dd217df6aa61e296309af2f929ad80fd", - "revision": 42 + "schemaHash": "4c069fb88b4e8d69ffb38c74a978955d21d2fb20022266d298f1d9855f45ba78", + "revision": 43 } \ No newline at end of file diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 46b90583..db11b0a8 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -134,10 +134,10 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } private getDummyValueIfErrorIgnored(args: CcApiContextQuery): Record | undefined { - if (!('ignoreErrorOnMissingContext' in args) || !args.ignoreErrorOnMissingContext) { + if (!args.ignoreErrorOnMissingContext) { return undefined; } - if (!('dummyValue' in args) || !Array.isArray(args.dummyValue) || args.dummyValue.length === 0) { + if (!Array.isArray(args.dummyValue) || args.dummyValue.length === 0) { return undefined; } const dummyValue = args.dummyValue[0]; diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index 925457e2..168fd21c 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -261,7 +261,7 @@ describe('dummy value', () => { StorageEncrypted: 'true', }, ], - } as TestQueryWithDummy); + }); // THEN expect(results.length).toEqual(1); @@ -290,7 +290,7 @@ describe('dummy value', () => { StorageEncrypted: 'true', }, ], - } as TestQueryWithDummy); + }); // THEN expect(results.length).toEqual(1); @@ -319,7 +319,7 @@ describe('dummy value', () => { StorageEncrypted: 'true', }, ], - } as TestQueryWithDummy), + }), ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); @@ -342,7 +342,7 @@ describe('dummy value', () => { StorageEncrypted: 'true', }, ], - } as TestQueryWithDummy), + }), ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); @@ -359,7 +359,7 @@ describe('dummy value', () => { exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], ignoreErrorOnMissingContext: true, - } as TestQueryWithDummy), + }), ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); @@ -380,7 +380,7 @@ describe('dummy value', () => { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', StorageEncrypted: 'true', }, - } as TestQueryWithDummy), + }), ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); @@ -398,7 +398,7 @@ describe('dummy value', () => { propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], ignoreErrorOnMissingContext: true, dummyValue: [], - } as TestQueryWithDummy), + }), ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); @@ -418,13 +418,8 @@ describe('dummy value', () => { dummyValue: [ 'not an object', ], - } as TestQueryWithDummy), + }), ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); }); /* eslint-enable */ - -interface TestQueryWithDummy extends CcApiContextQuery { - ignoreErrorOnMissingContext?: boolean; - dummyValue?: any; -} From b54e689b169367b697d0b48f58ab172c7c0f2664 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:43:47 +0900 Subject: [PATCH 07/19] rm unused import --- packages/aws-cdk/test/context-providers/cc-api-provider.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index 168fd21c..d469e030 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -1,7 +1,6 @@ import { GetResourceCommand, ListResourcesCommand } from '@aws-sdk/client-cloudcontrol'; import { CcApiContextProviderPlugin } from '../../lib/context-providers/cc-api-provider'; import { mockCloudControlClient, MockSdkProvider, restoreSdkMocksToDefault } from '../util/mock-sdk'; -import { CcApiContextQuery } from '@aws-cdk/cloud-assembly-schema'; let provider: CcApiContextProviderPlugin; From 0262d51d508cab4f90e9f46ddb6377104f3844a0 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 23 Mar 2025 02:34:35 +0900 Subject: [PATCH 08/19] modify by review --- .../lib/cloud-assembly/context-queries.ts | 4 +- .../schema/cloud-assembly.schema.json | 4 +- .../cloud-assembly-schema/schema/version.json | 4 +- .../lib/context-providers/cc-api-provider.ts | 58 +++++----- .../context-providers/cc-api-provider.test.ts | 102 ++++++++++++++---- 5 files changed, 119 insertions(+), 53 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index f25ce77b..3078c8ab 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -392,10 +392,10 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { readonly dummyValue?: any; /** - * When True, the context provider don't throw an error and return the dummyValue if the resource was not found. + * Ignore an error and return the `dummyValue` instead if the resource was not found. * @default false */ - readonly ignoreErrorOnMissingContext?: boolean; + readonly ignoreFailedLookup?: boolean; } /** diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index e73d0294..01eb0095 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -1049,8 +1049,8 @@ "dummyValue": { "description": "The value to return if the context value was not found. (Default - None)" }, - "ignoreErrorOnMissingContext": { - "description": "When True, the context provider don't throw an error and return the dummyValue if the resource was not found.", + "ignoreFailedLookup": { + "description": "Ignore an error and return the `dummyValue` instead if the resource was not found.", "default": false, "type": "boolean" }, diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index a65b95de..89a9dae2 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { - "schemaHash": "4c069fb88b4e8d69ffb38c74a978955d21d2fb20022266d298f1d9855f45ba78", - "revision": 43 + "schemaHash": "5fe173a6d0d4d783245c472e0c03c2b6d7d216ace91b35635b8f7a3dacd2fd7e", + "revision": 42 } \ No newline at end of file diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 0acd3ce5..7af42f85 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -4,6 +4,7 @@ import { type SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-pr import type { ContextProviderPlugin } from '../api/plugin'; import { ContextProviderError } from '../toolkit/error'; import { findJsonValue, getResultObj } from '../util'; +import { ResourceNotFoundException } from '@aws-sdk/client-cloudcontrol'; export class CcApiContextProviderPlugin implements ContextProviderPlugin { constructor(private readonly aws: SdkProvider) { @@ -19,8 +20,18 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { public async getValue(args: CcApiContextQuery) { const cloudControl = (await initContextProviderSdk(this.aws, args)).cloudControl(); - const result = await this.findResources(cloudControl, args); - return result; + try { + const result = await this.findResources(cloudControl, args); + return result; + } catch (err) { + if (err instanceof ResourceNotFoundException) { + const dummyObject = this.getDummyObject(args); + if (dummyObject) { + return [dummyObject]; + } + } + throw err; + } } private async findResources(cc: ICloudControlClient, args: CcApiContextQuery): Promise<{[key: string]: any} []> { @@ -31,20 +42,12 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { throw new ContextProviderError(`Neither exactIdentifier nor propertyMatch is specified. Failed to find resources using CC API for type ${args.typeName}.`); } - try { - if (args.exactIdentifier) { - // use getResource to get the exact indentifier - return await this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn); - } else { - // use listResource - return await this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn); - } - } catch (err) { - const dummyValue = this.getDummyValueIfErrorIgnored(args); - if (dummyValue) { - return [getResultObj(dummyValue, 'dummy-id', args.propertiesToReturn)]; - } - throw err; + if (args.exactIdentifier) { + // use getResource to get the exact indentifier + return await this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn, args.ignoreFailedLookup); + } else { + // use listResource + return await this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn, args.ignoreFailedLookup); } } @@ -60,6 +63,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { typeName: string, exactIdentifier: string, propertiesToReturn: string[], + ignoreFailedLookup?: boolean, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; try { @@ -76,6 +80,9 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { throw new ContextProviderError(`Could not get resource ${exactIdentifier}.`); } } catch (err) { + if (err instanceof ResourceNotFoundException && ignoreFailedLookup) { + throw err; + } throw new ContextProviderError(`Encountered CC API error while getting resource ${exactIdentifier}. Error: ${err}`); } return resultObjs; @@ -93,6 +100,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { typeName: string, propertyMatch: Record, propertiesToReturn: string[], + ignoreFailedLookup?: boolean, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; @@ -128,22 +136,18 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } }); } catch (err) { + if (err instanceof ResourceNotFoundException && ignoreFailedLookup) { + throw err; + } throw new ContextProviderError(`Could not get resources ${JSON.stringify(propertyMatch)}. Error: ${err}`); } return resultObjs; } - private getDummyValueIfErrorIgnored(args: CcApiContextQuery): Record | undefined { - if (!args.ignoreErrorOnMissingContext) { - return undefined; - } - if (!Array.isArray(args.dummyValue) || args.dummyValue.length === 0) { - return undefined; - } - const dummyValue = args.dummyValue[0]; - if (typeof dummyValue !== 'object' || dummyValue === null) { - return undefined; + private getDummyObject(args: CcApiContextQuery): Record | undefined { + if (!Array.isArray(args.dummyValue) || args.dummyValue.length === 0 || typeof args.dummyValue[0] !== 'object' || args.dummyValue[0] === null) { + throw new ContextProviderError(`dummyValue must be an array with at least one object. Failed to get dummy object for type ${args.typeName}.`); } - return dummyValue; + return getResultObj(args.dummyValue[0], 'dummy-id', args.propertiesToReturn); } } diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index d469e030..a2a999cf 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -1,4 +1,4 @@ -import { GetResourceCommand, ListResourcesCommand } from '@aws-sdk/client-cloudcontrol'; +import { GetResourceCommand, InvalidRequestException, ListResourcesCommand, ResourceNotFoundException } from '@aws-sdk/client-cloudcontrol'; import { CcApiContextProviderPlugin } from '../../lib/context-providers/cc-api-provider'; import { mockCloudControlClient, MockSdkProvider, restoreSdkMocksToDefault } from '../util/mock-sdk'; @@ -244,7 +244,7 @@ test('error by specifying neither exactIdentifier or propertyMatch', async () => describe('dummy value', () => { test('returns dummy value when CC API getResource fails', async () => { // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); // WHEN const results = await provider.getValue({ @@ -253,7 +253,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: true, + ignoreFailedLookup: true, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -273,7 +273,7 @@ describe('dummy value', () => { test('returns dummy value when CC API listResources fails', async () => { // GIVEN - mockCloudControlClient.on(ListResourcesCommand).rejects('No data found'); + mockCloudControlClient.on(ListResourcesCommand).rejects(createResourceNotFoundException()); // WHEN const results = await provider.getValue({ @@ -282,7 +282,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', propertyMatch: { 'StorageEncrypted': 'true' }, propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: true, + ignoreFailedLookup: true, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -300,9 +300,55 @@ describe('dummy value', () => { }); }); - test('throws error when CC API fails and ignoreErrorOnMissingContext is not provided', async () => { + test('throws error when CC API getResource fails but the error is not ResourceNotFoundException', async () => { // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + mockCloudControlClient.on(GetResourceCommand).rejects(createOtherError()); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + exactIdentifier: 'bad-identifier', + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreFailedLookup: true, + dummyValue: [ + { + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + }, + ], + }), + ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + }); + + test('throws error when CC API listResources fails but the error is not ResourceNotFoundException', async () => { + // GIVEN + mockCloudControlClient.on(ListResourcesCommand).rejects(createOtherError()); + + // WHEN/THEN + await expect( + provider.getValue({ + account: '123456789012', + region: 'us-east-1', + typeName: 'AWS::RDS::DBInstance', + propertyMatch: { 'StorageEncrypted': 'true' }, + propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], + ignoreFailedLookup: true, + dummyValue: [ + { + DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', + StorageEncrypted: 'true', + }, + ], + }), + ).rejects.toThrow('Could not get resources {"StorageEncrypted":"true"}.'); + }); + + test('throws error when CC API fails and ignoreFailedLookup is not provided', async () => { + // GIVEN + mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); // WHEN/THEN await expect( @@ -322,9 +368,9 @@ describe('dummy value', () => { ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); - test('throws error when CC API fails and ignoreErrorOnMissingContext is false', async () => { + test('throws error when CC API fails and ignoreFailedLookup is false', async () => { // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); // WHEN/THEN await expect( @@ -334,7 +380,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: false, + ignoreFailedLookup: false, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -357,14 +403,14 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: true, + ignoreFailedLookup: true, }), ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); test('throws error when CC API fails and dummyValue is not an array', async () => { // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); // WHEN/THEN await expect( @@ -374,18 +420,18 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: true, + ignoreFailedLookup: true, dummyValue: { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', StorageEncrypted: 'true', }, }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is an empty array', async () => { // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); // WHEN/THEN await expect( @@ -395,15 +441,15 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: true, + ignoreFailedLookup: true, dummyValue: [], }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is not an object array', async () => { // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); // WHEN/THEN await expect( @@ -413,12 +459,28 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: true, + ignoreFailedLookup: true, dummyValue: [ 'not an object', ], }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); }); /* eslint-enable */ + +function createResourceNotFoundException() { + return new ResourceNotFoundException({ + $metadata: {}, + message: 'Resource not found', + Message: 'Resource not found' + }); +} + +function createOtherError() { + return new InvalidRequestException({ + $metadata: {}, + message: 'Other error', + Message: 'Other error' + }); +} From bb905c6a9181186594cc631a9f55ce381dde229e Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 23 Mar 2025 03:04:19 +0900 Subject: [PATCH 09/19] modify a test --- .../aws-cdk/test/context-providers/cc-api-provider.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index a2a999cf..0a84eed5 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -393,7 +393,7 @@ describe('dummy value', () => { test('throws error when CC API fails and dummyValue is not provided', async () => { // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects('No data found'); + mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); // WHEN/THEN await expect( @@ -405,7 +405,7 @@ describe('dummy value', () => { propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], ignoreFailedLookup: true, }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is not an array', async () => { From c3154974a64ac6bb558c59284bfdf49e5fe604bf Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 23 Mar 2025 03:21:38 +0900 Subject: [PATCH 10/19] change getDummyObject --- .../lib/context-providers/cc-api-provider.ts | 13 ++++++++----- .../test/context-providers/cc-api-provider.test.ts | 8 ++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 7af42f85..1c116b3e 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -27,7 +27,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { if (err instanceof ResourceNotFoundException) { const dummyObject = this.getDummyObject(args); if (dummyObject) { - return [dummyObject]; + return dummyObject; } } throw err; @@ -144,10 +144,13 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { return resultObjs; } - private getDummyObject(args: CcApiContextQuery): Record | undefined { - if (!Array.isArray(args.dummyValue) || args.dummyValue.length === 0 || typeof args.dummyValue[0] !== 'object' || args.dummyValue[0] === null) { - throw new ContextProviderError(`dummyValue must be an array with at least one object. Failed to get dummy object for type ${args.typeName}.`); + private getDummyObject(args: CcApiContextQuery): {[key: string]: any}[] { + if (!Array.isArray(args.dummyValue) || args.dummyValue.length === 0 || !args.dummyValue.every(value => typeof value === 'object' && value !== null)) { + throw new ContextProviderError(`dummyValue must be an array of objects. Failed to get dummy object for type ${args.typeName}.`); } - return getResultObj(args.dummyValue[0], 'dummy-id', args.propertiesToReturn); + + return args.dummyValue.map((dummyValue) => + getResultObj(dummyValue, `dummy-id`, args.propertiesToReturn) + ); } } diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index 0a84eed5..59efb29b 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -405,7 +405,7 @@ describe('dummy value', () => { propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], ignoreFailedLookup: true, }), - ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is not an array', async () => { @@ -426,7 +426,7 @@ describe('dummy value', () => { StorageEncrypted: 'true', }, }), - ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is an empty array', async () => { @@ -444,7 +444,7 @@ describe('dummy value', () => { ignoreFailedLookup: true, dummyValue: [], }), - ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is not an object array', async () => { @@ -464,7 +464,7 @@ describe('dummy value', () => { 'not an object', ], }), - ).rejects.toThrow('dummyValue must be an array with at least one object. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); }); }); /* eslint-enable */ From 407cdb2a8c435ea1d07c85efd22464de78b18021 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 23 Mar 2025 03:28:16 +0900 Subject: [PATCH 11/19] change description of dummyValue --- .../cloud-assembly-schema/lib/cloud-assembly/context-queries.ts | 2 +- .../cloud-assembly-schema/schema/cloud-assembly.schema.json | 2 +- packages/@aws-cdk/cloud-assembly-schema/schema/version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index 3078c8ab..f8a6854d 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -386,7 +386,7 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { readonly propertiesToReturn: string[]; /** - * The value to return if the context value was not found. + * The value to return if the resource was not found and `ignoreFailedLookup` is true. * @default - None */ readonly dummyValue?: any; diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 01eb0095..382fb66d 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -1047,7 +1047,7 @@ } }, "dummyValue": { - "description": "The value to return if the context value was not found. (Default - None)" + "description": "The value to return if the resource was not found and `ignoreFailedLookup` is true. (Default - None)" }, "ignoreFailedLookup": { "description": "Ignore an error and return the `dummyValue` instead if the resource was not found.", diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 89a9dae2..1cb04351 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { - "schemaHash": "5fe173a6d0d4d783245c472e0c03c2b6d7d216ace91b35635b8f7a3dacd2fd7e", + "schemaHash": "71bdf8c99c7f07e515b118a81c9900b0eb38763c221f64b78c8097810f501422", "revision": 42 } \ No newline at end of file From 8d9d94cd2e29efce845ee53a525ddc6ee72417ce Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 23 Mar 2025 03:33:43 +0900 Subject: [PATCH 12/19] getDummyObject to getDummyObjects fix test --- .../aws-cdk/lib/context-providers/cc-api-provider.ts | 10 +++++----- .../test/context-providers/cc-api-provider.test.ts | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 1c116b3e..04111c79 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -25,9 +25,9 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { return result; } catch (err) { if (err instanceof ResourceNotFoundException) { - const dummyObject = this.getDummyObject(args); - if (dummyObject) { - return dummyObject; + const dummyObjects = this.getDummyObjects(args); + if (dummyObjects) { + return dummyObjects; } } throw err; @@ -144,9 +144,9 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { return resultObjs; } - private getDummyObject(args: CcApiContextQuery): {[key: string]: any}[] { + private getDummyObjects(args: CcApiContextQuery): {[key: string]: any}[] { if (!Array.isArray(args.dummyValue) || args.dummyValue.length === 0 || !args.dummyValue.every(value => typeof value === 'object' && value !== null)) { - throw new ContextProviderError(`dummyValue must be an array of objects. Failed to get dummy object for type ${args.typeName}.`); + throw new ContextProviderError(`dummyValue must be an array of objects. Failed to get dummy objects for type ${args.typeName}.`); } return args.dummyValue.map((dummyValue) => diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index 59efb29b..e6621789 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -405,7 +405,7 @@ describe('dummy value', () => { propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], ignoreFailedLookup: true, }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is not an array', async () => { @@ -426,7 +426,7 @@ describe('dummy value', () => { StorageEncrypted: 'true', }, }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is an empty array', async () => { @@ -444,7 +444,7 @@ describe('dummy value', () => { ignoreFailedLookup: true, dummyValue: [], }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); }); test('throws error when CC API fails and dummyValue is not an object array', async () => { @@ -464,7 +464,7 @@ describe('dummy value', () => { 'not an object', ], }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy object for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); }); }); /* eslint-enable */ From 1a933f01fbf84381d920b8581bff43c843402306 Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 23 Mar 2025 03:42:54 +0900 Subject: [PATCH 13/19] rm await --- packages/aws-cdk/lib/context-providers/cc-api-provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 04111c79..21a022af 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -44,10 +44,10 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { if (args.exactIdentifier) { // use getResource to get the exact indentifier - return await this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn, args.ignoreFailedLookup); + return this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn, args.ignoreFailedLookup); } else { // use listResource - return await this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn, args.ignoreFailedLookup); + return this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn, args.ignoreFailedLookup); } } From 4ea2594cc993342553293e98ea5bb0edf0ae810b Mon Sep 17 00:00:00 2001 From: go-to-k <24818752+go-to-k@users.noreply.github.com> Date: Sun, 23 Mar 2025 04:18:28 +0900 Subject: [PATCH 14/19] revert property name to ignoreErrorOnMissingContext --- .../lib/cloud-assembly/context-queries.ts | 4 ++-- .../schema/cloud-assembly.schema.json | 4 ++-- .../cloud-assembly-schema/schema/version.json | 2 +- .../lib/context-providers/cc-api-provider.ts | 12 +++++----- .../context-providers/cc-api-provider.test.ts | 22 +++++++++---------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index f8a6854d..3b0a2205 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -386,7 +386,7 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { readonly propertiesToReturn: string[]; /** - * The value to return if the resource was not found and `ignoreFailedLookup` is true. + * The value to return if the resource was not found and `ignoreErrorOnMissingContext` is true. * @default - None */ readonly dummyValue?: any; @@ -395,7 +395,7 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { * Ignore an error and return the `dummyValue` instead if the resource was not found. * @default false */ - readonly ignoreFailedLookup?: boolean; + readonly ignoreErrorOnMissingContext?: boolean; } /** diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 382fb66d..99ec39e0 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -1047,9 +1047,9 @@ } }, "dummyValue": { - "description": "The value to return if the resource was not found and `ignoreFailedLookup` is true. (Default - None)" + "description": "The value to return if the resource was not found and `ignoreErrorOnMissingContext` is true. (Default - None)" }, - "ignoreFailedLookup": { + "ignoreErrorOnMissingContext": { "description": "Ignore an error and return the `dummyValue` instead if the resource was not found.", "default": false, "type": "boolean" diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 1cb04351..23d6ca51 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { - "schemaHash": "71bdf8c99c7f07e515b118a81c9900b0eb38763c221f64b78c8097810f501422", + "schemaHash": "7c650e458026e83b3de23aef106137c69f10031147873e2638b6d8e078419777", "revision": 42 } \ No newline at end of file diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index e7fe7d37..4558e30b 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -44,10 +44,10 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { if (args.exactIdentifier) { // use getResource to get the exact indentifier - return this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn, args.ignoreFailedLookup); + return this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn, args.ignoreErrorOnMissingContext); } else { // use listResource - return this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn, args.ignoreFailedLookup); + return this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn, args.ignoreErrorOnMissingContext); } } @@ -63,7 +63,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { typeName: string, exactIdentifier: string, propertiesToReturn: string[], - ignoreFailedLookup?: boolean, + ignoreErrorOnMissingContext?: boolean, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; try { @@ -80,7 +80,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { throw new ContextProviderError(`Could not get resource ${exactIdentifier}.`); } } catch (err) { - if (err instanceof ResourceNotFoundException && ignoreFailedLookup) { + if (err instanceof ResourceNotFoundException && ignoreErrorOnMissingContext) { throw err; } throw new ContextProviderError(`Encountered CC API error while getting resource ${exactIdentifier}. Error: ${err}`); @@ -100,7 +100,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { typeName: string, propertyMatch: Record, propertiesToReturn: string[], - ignoreFailedLookup?: boolean, + ignoreErrorOnMissingContext?: boolean, ): Promise<{[key: string]: any}[]> { const resultObjs: {[key: string]: any}[] = []; @@ -136,7 +136,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { } }); } catch (err) { - if (err instanceof ResourceNotFoundException && ignoreFailedLookup) { + if (err instanceof ResourceNotFoundException && ignoreErrorOnMissingContext) { throw err; } throw new ContextProviderError(`Could not get resources ${JSON.stringify(propertyMatch)}. Error: ${err}`); diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index e6621789..e98a6262 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -253,7 +253,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -282,7 +282,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', propertyMatch: { 'StorageEncrypted': 'true' }, propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -312,7 +312,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -335,7 +335,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', propertyMatch: { 'StorageEncrypted': 'true' }, propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -346,7 +346,7 @@ describe('dummy value', () => { ).rejects.toThrow('Could not get resources {"StorageEncrypted":"true"}.'); }); - test('throws error when CC API fails and ignoreFailedLookup is not provided', async () => { + test('throws error when CC API fails and ignoreErrorOnMissingContext is not provided', async () => { // GIVEN mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); @@ -368,7 +368,7 @@ describe('dummy value', () => { ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); }); - test('throws error when CC API fails and ignoreFailedLookup is false', async () => { + test('throws error when CC API fails and ignoreErrorOnMissingContext is false', async () => { // GIVEN mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); @@ -380,7 +380,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: false, + ignoreErrorOnMissingContext: false, dummyValue: [ { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', @@ -403,7 +403,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, }), ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); }); @@ -420,7 +420,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, dummyValue: { DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', StorageEncrypted: 'true', @@ -441,7 +441,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, dummyValue: [], }), ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); @@ -459,7 +459,7 @@ describe('dummy value', () => { typeName: 'AWS::RDS::DBInstance', exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreFailedLookup: true, + ignoreErrorOnMissingContext: true, dummyValue: [ 'not an object', ], From 52d95abfb7499610d45fb0acfc2d417299be9b4f Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 24 Mar 2025 13:54:42 +0100 Subject: [PATCH 15/19] Refactor to combine code paths a bit more, and remove tests I don't want to part of the contract Failing `listResources` is not possible yet, that will be added later. --- .../lib/cloud-assembly/context-queries.ts | 61 +++++- .../lib/context-providers/cc-api-provider.ts | 195 +++++++++--------- .../context-providers/cc-api-provider.test.ts | 71 ++----- 3 files changed, 168 insertions(+), 159 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index 3b0a2205..a8f9939f 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -365,39 +365,84 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { readonly typeName: string; /** - * exactIdentifier of the resource. - * Specifying exactIdentifier will return at most one result. - * Either exactIdentifier or propertyMatch should be specified. - * @default - None + * Identifier of the resource to look up using `GetResource`. + * + * Specifying exactIdentifier will return exactly one result, or throw an error. + * + * + * @default - Either exactIdentifier or propertyMatch should be specified. */ readonly exactIdentifier?: string; /** - * This indicates the property to search for. + * Returns any resources matching these properties, using `ListResources`. + * * Specifying propertyMatch will return 0 or more results. - * Either exactIdentifier or propertyMatch should be specified. - * @default - None + * + * ## Notes on property completeness + * + * CloudControl API's `ListResources` may return fewer properties than + * `GetResource` would, depending on the resource implementation. + * + * The resources that `propertyMatch` matches against will *only ever* be the + * properties returned by the `ListResources` call. + * + * @default - Either exactIdentifier or propertyMatch should be specified. */ readonly propertyMatch?: Record; /** * This is a set of properties returned from CC API that we want to return from ContextQuery. + * + * If any properties listed here are absent from the target resource, an error will be thrown. + * + * The returned object will always include the key `Identifier` with the CC-API returned + * field `Identifier`. + * + * ## Notes on property completeness + * + * CloudControl API's `ListResources` may return fewer properties than + * `GetResource` would, depending on the resource implementation. + * + * The returned properties here are *currently* selected from the response + * object that CloudControl API returns to the CDK CLI. + * + * However, if we find there is need to do so, we may decide to change this + * behavior in the future: we might change it to perform an additional + * `GetResource` call for resources matched by `propertyMatch`. */ readonly propertiesToReturn: string[]; /** * The value to return if the resource was not found and `ignoreErrorOnMissingContext` is true. - * @default - None + * + * If supplied, `dummyValue` should be an array of objects. + * + * `dummyValue` does not have to have elements, and it may have objects with + * different properties than the properties in `propertiesToReturn`, but it + * will be easiest for downstream code if the `dummyValue` conforms to + * the expected response shape. + * + * @default - No dummy value available */ readonly dummyValue?: any; /** * Ignore an error and return the `dummyValue` instead if the resource was not found. + * + * - In case of an `exactIdentifier` lookup, return the `dummyValue` if the resource with + * that identifier was not found. + * - In case of a `propertyMatch` lookup, this setting currently does not have any effect, + * as `propertyMatch` queries can legally return 0 resources. + * + * if `ignoreErrorOnMissingContext` is set, `dummyValue` should be set and be an array. + * * @default false */ readonly ignoreErrorOnMissingContext?: boolean; } + /** * Query input for plugins * diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 4558e30b..0be15f44 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -4,7 +4,7 @@ import type { ICloudControlClient } from '../api'; import { type SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import type { ContextProviderPlugin } from '../api/plugin'; import { findJsonValue, getResultObj } from '../util'; -import { ResourceNotFoundException } from '@aws-sdk/client-cloudcontrol'; +import { ResourceDescription, ResourceNotFoundException } from '@aws-sdk/client-cloudcontrol'; export class CcApiContextProviderPlugin implements ContextProviderPlugin { constructor(private readonly aws: SdkProvider) { @@ -12,145 +12,150 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { /** * This returns a data object with the value from CloudControl API result. - * args.typeName - see https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/supported-resources.html - * args.exactIdentifier - use CC API getResource. - * args.propertyMatch - use CCP API listResources to get resources and propertyMatch to search through the list. - * args.propertiesToReturn - Properties from CC API to return. + * + * See the documentation in the Cloud Assembly Schema for the semantics of + * each query parameter. */ public async getValue(args: CcApiContextQuery) { + // Validate input + if (args.exactIdentifier && args.propertyMatch) { + throw new ContextProviderError(`Provider protocol error: specify either exactIdentifier or propertyMatch, but not both (got ${JSON.stringify(args)})`); + } + if (args.ignoreErrorOnMissingContext && args.dummyValue === undefined) { + throw new ContextProviderError(`Provider protocol error: if ignoreErrorOnMissingContext is set, a dummyValue must be supplied (got ${JSON.stringify(args)})`); + } + if (args.dummyValue !== undefined && (!Array.isArray(args.dummyValue) || !args.dummyValue.every(isObject))) { + throw new ContextProviderError(`Provider protocol error: dummyValue must be an array of objects (got ${JSON.stringify(args.dummyValue)})`); + } + + // Do the lookup const cloudControl = (await initContextProviderSdk(this.aws, args)).cloudControl(); - try { - const result = await this.findResources(cloudControl, args); - return result; + try { + let resources: FoundResource[]; + if (args.exactIdentifier) { + // use getResource to get the exact indentifier + resources = await this.getResource(cloudControl, args.typeName, args.exactIdentifier); + } else if (args.propertyMatch) { + // use listResource + resources = await this.listResources(cloudControl, args.typeName, args.propertyMatch); + } else { + throw new ContextProviderError(`Provider protocol error: neither exactIdentifier nor propertyMatch is specified in ${JSON.stringify(args)}.`); + } + + return resources.map((r) => getResultObj(r.properties, r.identifier, args.propertiesToReturn)); } catch (err) { - if (err instanceof ResourceNotFoundException) { - const dummyObjects = this.getDummyObjects(args); - if (dummyObjects) { - return dummyObjects; - } + if (err instanceof ZeroResourcesFoundError && args.ignoreErrorOnMissingContext) { + // We've already type-checked dummyValue. + return args.dummyValue; } throw err; } } - private async findResources(cc: ICloudControlClient, args: CcApiContextQuery): Promise<{[key: string]: any} []> { - if (args.exactIdentifier && args.propertyMatch) { - throw new ContextProviderError(`Specify either exactIdentifier or propertyMatch, but not both. Failed to find resources using CC API for type ${args.typeName}.`); - } - if (!args.exactIdentifier && !args.propertyMatch) { - throw new ContextProviderError(`Neither exactIdentifier nor propertyMatch is specified. Failed to find resources using CC API for type ${args.typeName}.`); - } - - if (args.exactIdentifier) { - // use getResource to get the exact indentifier - return this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn, args.ignoreErrorOnMissingContext); - } else { - // use listResource - return this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn, args.ignoreErrorOnMissingContext); - } - } - /** * Calls getResource from CC API to get the resource. * See https://docs.aws.amazon.com/cli/latest/reference/cloudcontrol/get-resource.html * - * If the exactIdentifier is not found, then an empty map is returned. - * If the resource is found, then a map of the identifier to a map of property values is returned. + * Will always return exactly one resource, or fail. */ private async getResource( cc: ICloudControlClient, typeName: string, exactIdentifier: string, - propertiesToReturn: string[], - ignoreErrorOnMissingContext?: boolean, - ): Promise<{[key: string]: any}[]> { - const resultObjs: {[key: string]: any}[] = []; + ): Promise { try { const result = await cc.getResource({ TypeName: typeName, Identifier: exactIdentifier, }); - const id = result.ResourceDescription?.Identifier ?? ''; - if (id !== '') { - const propsObject = JSON.parse(result.ResourceDescription?.Properties ?? ''); - const propsObj = getResultObj(propsObject, result.ResourceDescription?.Identifier!, propertiesToReturn); - resultObjs.push(propsObj); - } else { - throw new ContextProviderError(`Could not get resource ${exactIdentifier}.`); + if (!result.ResourceDescription) { + throw new ContextProviderError(`Unexpected CloudControl API behavior: returned empty response`); } - } catch (err) { - if (err instanceof ResourceNotFoundException && ignoreErrorOnMissingContext) { - throw err; + + return [foundResourceFromCcApi(result.ResourceDescription)]; + } catch (err: any) { + if (err instanceof ResourceNotFoundException || (err as any).name === 'ResourceNotFoundException') { + throw new ZeroResourcesFoundError(`No resource of type ${typeName} with identifier: ${exactIdentifier}`); } - throw new ContextProviderError(`Encountered CC API error while getting resource ${exactIdentifier}. Error: ${err}`); + if (!(err instanceof ContextProviderError)) { + throw new ContextProviderError(`Encountered CC API error while getting ${typeName} resource ${exactIdentifier}: ${err.message}`); + } + throw err; } - return resultObjs; } /** * Calls listResources from CC API to get the resources and apply args.propertyMatch to find the resources. * See https://docs.aws.amazon.com/cli/latest/reference/cloudcontrol/list-resources.html * - * Since exactIdentifier is not specified, propertyMatch must be specified. - * This returns an object where the ids are object keys and values are objects with keys of args.propertiesToReturn. + * Will return 0 or more resources. + * + * Does not currently paginate through more than one result page. */ private async listResources( cc: ICloudControlClient, typeName: string, propertyMatch: Record, - propertiesToReturn: string[], - ignoreErrorOnMissingContext?: boolean, - ): Promise<{[key: string]: any}[]> { - const resultObjs: {[key: string]: any}[] = []; - + ): Promise { try { const result = await cc.listResources({ TypeName: typeName, + }); - result.ResourceDescriptions?.forEach((resource) => { - const id = resource.Identifier ?? ''; - if (id !== '') { - const propsObject = JSON.parse(resource.Properties ?? ''); - - const filters = Object.entries(propertyMatch); - let match = false; - if (filters) { - match = filters.every((record, _index, _arr) => { - const key = record[0]; - const expected = record[1]; - const actual = findJsonValue(propsObject, key); - return propertyMatchesFilter(actual, expected); - }); - - function propertyMatchesFilter(actual: any, expected: unknown) { - // For now we just check for strict equality, but we can implement pattern matching and fuzzy matching here later - return expected === actual; - } - } - - if (match) { - const propsObj = getResultObj(propsObject, resource.Identifier!, propertiesToReturn); - resultObjs.push(propsObj); - } - } - }); - } catch (err) { - if (err instanceof ResourceNotFoundException && ignoreErrorOnMissingContext) { - throw err; + const found = (result.ResourceDescriptions ?? []) + .map(foundResourceFromCcApi) + .filter((r) => { + return Object.entries(propertyMatch).every(([propPath, expected]) => { + const actual = findJsonValue(r.properties, propPath); + return propertyMatchesFilter(actual, expected); + }); + }); + + + return found; + } catch (err: any) { + if (!(err instanceof ContextProviderError) && !(err instanceof ZeroResourcesFoundError)) { + throw new ContextProviderError(`Encountered CC API error while listing ${typeName} resources matching ${JSON.stringify(propertyMatch)}: ${err.message}`); } - throw new ContextProviderError(`Could not get resources ${JSON.stringify(propertyMatch)}. Error: ${err}`); + throw err; } - return resultObjs; } +} - private getDummyObjects(args: CcApiContextQuery): {[key: string]: any}[] { - if (!Array.isArray(args.dummyValue) || args.dummyValue.length === 0 || !args.dummyValue.every(value => typeof value === 'object' && value !== null)) { - throw new ContextProviderError(`dummyValue must be an array of objects. Failed to get dummy objects for type ${args.typeName}.`); - } +/** + * Convert a CC API response object into a nicer object (parse the JSON) + */ +function foundResourceFromCcApi(desc: ResourceDescription): FoundResource { + return { + identifier: desc.Identifier ?? '*MISSING*', + properties: JSON.parse(desc.Properties ?? '{}'), + }; +} - return args.dummyValue.map((dummyValue) => - getResultObj(dummyValue, `dummy-id`, args.propertiesToReturn) - ); - } +/** + * Whether the given property value matches the given filter + * + * For now we just check for strict equality, but we can implement pattern matching and fuzzy matching here later + */ +function propertyMatchesFilter(actual: unknown, expected: unknown) { + return expected === actual; } + +function isObject(x: unknown): x is {[key: string]: unknown} { + return typeof x === 'object' && x !== null && !Array.isArray(x); +} + +/** + * A parsed version of the return value from CCAPI + */ +interface FoundResource { + readonly identifier: string; + readonly properties: Record; +} + +/** + * A specific lookup failure indicating 0 resources found that can be recovered + */ +class ZeroResourcesFoundError extends Error { +} \ No newline at end of file diff --git a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts index e98a6262..f425bb19 100644 --- a/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts +++ b/packages/aws-cdk/test/context-providers/cc-api-provider.test.ts @@ -38,30 +38,6 @@ test('looks up RDS instance using CC API getResource', async () => { })); }); -// In theory, this should never happen. We ask for my-db-instance-1 but CC API returns ''. -// Included this to test the code path. -test('looks up RDS instance using CC API getResource - wrong match', async () => { - // GIVEN - mockCloudControlClient.on(GetResourceCommand).resolves({ - TypeName: 'AWS::RDS::DBInstance', - ResourceDescription: { - Identifier: '', - Properties: '{"DBInstanceArn":"arn:aws:rds:us-east-1:123456789012:db:test-instance-1","StorageEncrypted":"true"}', - }, - }); - - await expect( - // WHEN - provider.getValue({ - account: '123456789012', - region: 'us-east-1', - typeName: 'AWS::RDS::DBInstance', - exactIdentifier: 'my-db-instance-1', - propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - }), - ).rejects.toThrow('Encountered CC API error while getting resource my-db-instance-1.'); // THEN -}); - test('looks up RDS instance using CC API getResource - empty response', async () => { // GIVEN mockCloudControlClient.on(GetResourceCommand).resolves({ @@ -76,7 +52,7 @@ test('looks up RDS instance using CC API getResource - empty response', async () exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); // THEN + ).rejects.toThrow('Unexpected CloudControl API behavior: returned empty response'); // THEN }); test('looks up RDS instance using CC API getResource - error in CC API', async () => { @@ -92,7 +68,7 @@ test('looks up RDS instance using CC API getResource - error in CC API', async ( exactIdentifier: 'bad-identifier', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); // THEN + ).rejects.toThrow('Encountered CC API error while getting AWS::RDS::DBInstance resource bad-identifier'); // THEN }); test('looks up RDS instance using CC API listResources', async () => { @@ -201,7 +177,7 @@ test('looks up RDS instance using CC API listResources - error in CC API', async propertyMatch: { 'Endpoint.Port': '5432' }, propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], }), - ).rejects.toThrow('Could not get resources {"Endpoint.Port":"5432"}.'); // THEN + ).rejects.toThrow('error while listing AWS::RDS::DBInstance resources'); // THEN }); test('error by specifying both exactIdentifier and propertyMatch', async () => { @@ -222,7 +198,7 @@ test('error by specifying both exactIdentifier and propertyMatch', async () => { }, propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], }), - ).rejects.toThrow('Specify either exactIdentifier or propertyMatch, but not both. Failed to find resources using CC API for type AWS::RDS::DBInstance.'); // THEN + ).rejects.toThrow('specify either exactIdentifier or propertyMatch, but not both'); // THEN }); test('error by specifying neither exactIdentifier or propertyMatch', async () => { @@ -238,7 +214,7 @@ test('error by specifying neither exactIdentifier or propertyMatch', async () => typeName: 'AWS::RDS::DBInstance', propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], }), - ).rejects.toThrow('Neither exactIdentifier nor propertyMatch is specified. Failed to find resources using CC API for type AWS::RDS::DBInstance.'); // THEN + ).rejects.toThrow('neither exactIdentifier nor propertyMatch is specified'); }); describe('dummy value', () => { @@ -267,11 +243,12 @@ describe('dummy value', () => { expect(results[0]).toEqual({ DBInstanceArn: 'arn:aws:rds:us-east-1:123456789012:db:dummy-instance', StorageEncrypted: 'true', - Identifier: 'dummy-id', }); }); - test('returns dummy value when CC API listResources fails', async () => { + // TODO: This test can be re-enabled when listResources can be made to fail, after + // https://github.com/aws/aws-cdk-cli/pull/251 is merged. + test.skip('returns dummy value when CC API listResources fails', async () => { // GIVEN mockCloudControlClient.on(ListResourcesCommand).rejects(createResourceNotFoundException()); @@ -320,7 +297,7 @@ describe('dummy value', () => { }, ], }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + ).rejects.toThrow('Encountered CC API error while getting AWS::RDS::DBInstance resource bad-identifier: Other error'); }); test('throws error when CC API listResources fails but the error is not ResourceNotFoundException', async () => { @@ -343,7 +320,7 @@ describe('dummy value', () => { }, ], }), - ).rejects.toThrow('Could not get resources {"StorageEncrypted":"true"}.'); + ).rejects.toThrow('Encountered CC API error while listing AWS::RDS::DBInstance resources matching {\"StorageEncrypted\":\"true\"}: Other error'); }); test('throws error when CC API fails and ignoreErrorOnMissingContext is not provided', async () => { @@ -365,7 +342,7 @@ describe('dummy value', () => { }, ], }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + ).rejects.toThrow('No resource of type AWS::RDS::DBInstance with identifier: bad-identifier'); }); test('throws error when CC API fails and ignoreErrorOnMissingContext is false', async () => { @@ -388,7 +365,7 @@ describe('dummy value', () => { }, ], }), - ).rejects.toThrow('Encountered CC API error while getting resource bad-identifier.'); + ).rejects.toThrow('No resource of type AWS::RDS::DBInstance with identifier: bad-identifier'); }); test('throws error when CC API fails and dummyValue is not provided', async () => { @@ -405,7 +382,7 @@ describe('dummy value', () => { propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], ignoreErrorOnMissingContext: true, }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('if ignoreErrorOnMissingContext is set, a dummyValue must be supplied'); }); test('throws error when CC API fails and dummyValue is not an array', async () => { @@ -426,25 +403,7 @@ describe('dummy value', () => { StorageEncrypted: 'true', }, }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); - }); - - test('throws error when CC API fails and dummyValue is an empty array', async () => { - // GIVEN - mockCloudControlClient.on(GetResourceCommand).rejects(createResourceNotFoundException()); - - // WHEN/THEN - await expect( - provider.getValue({ - account: '123456789012', - region: 'us-east-1', - typeName: 'AWS::RDS::DBInstance', - exactIdentifier: 'bad-identifier', - propertiesToReturn: ['DBInstanceArn', 'StorageEncrypted'], - ignoreErrorOnMissingContext: true, - dummyValue: [], - }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects'); }); test('throws error when CC API fails and dummyValue is not an object array', async () => { @@ -464,7 +423,7 @@ describe('dummy value', () => { 'not an object', ], }), - ).rejects.toThrow('dummyValue must be an array of objects. Failed to get dummy objects for type AWS::RDS::DBInstance.'); + ).rejects.toThrow('dummyValue must be an array of objects'); }); }); /* eslint-enable */ From ae199f91ad884c5cdc0102fd4db5ed33c8621dae Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 24 Mar 2025 19:15:19 +0100 Subject: [PATCH 16/19] Some eslint work --- .../lib/cloud-assembly/context-queries.ts | 1 - .../schema/cloud-assembly.schema.json | 10 +++++----- .../@aws-cdk/cloud-assembly-schema/schema/version.json | 4 ++-- .../aws-cdk/lib/context-providers/cc-api-provider.ts | 8 ++++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index a8f9939f..b9194ec3 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -442,7 +442,6 @@ export interface CcApiContextQuery extends ContextLookupRoleOptions { readonly ignoreErrorOnMissingContext?: boolean; } - /** * Query input for plugins * diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 99ec39e0..22058718 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -1032,25 +1032,25 @@ "type": "string" }, "exactIdentifier": { - "description": "exactIdentifier of the resource.\nSpecifying exactIdentifier will return at most one result.\nEither exactIdentifier or propertyMatch should be specified. (Default - None)", + "description": "Identifier of the resource to look up using `GetResource`.\n\nSpecifying exactIdentifier will return exactly one result, or throw an error. (Default - Either exactIdentifier or propertyMatch should be specified.)", "type": "string" }, "propertyMatch": { - "description": "This indicates the property to search for.\nSpecifying propertyMatch will return 0 or more results.\nEither exactIdentifier or propertyMatch should be specified. (Default - None)", + "description": "Returns any resources matching these properties, using `ListResources`.\n\nSpecifying propertyMatch will return 0 or more results.\n\n## Notes on property completeness\n\nCloudControl API's `ListResources` may return fewer properties than\n`GetResource` would, depending on the resource implementation.\n\nThe resources that `propertyMatch` matches against will *only ever* be the\nproperties returned by the `ListResources` call. (Default - Either exactIdentifier or propertyMatch should be specified.)", "$ref": "#/definitions/Record" }, "propertiesToReturn": { - "description": "This is a set of properties returned from CC API that we want to return from ContextQuery.", + "description": "This is a set of properties returned from CC API that we want to return from ContextQuery.\n\nIf any properties listed here are absent from the target resource, an error will be thrown.\n\nThe returned object will always include the key `Identifier` with the CC-API returned\nfield `Identifier`.\n\n## Notes on property completeness\n\nCloudControl API's `ListResources` may return fewer properties than\n`GetResource` would, depending on the resource implementation.\n\nThe returned properties here are *currently* selected from the response\nobject that CloudControl API returns to the CDK CLI.\n\nHowever, if we find there is need to do so, we may decide to change this\nbehavior in the future: we might change it to perform an additional\n`GetResource` call for resources matched by `propertyMatch`.", "type": "array", "items": { "type": "string" } }, "dummyValue": { - "description": "The value to return if the resource was not found and `ignoreErrorOnMissingContext` is true. (Default - None)" + "description": "The value to return if the resource was not found and `ignoreErrorOnMissingContext` is true.\n\nIf supplied, `dummyValue` should be an array of objects.\n\n`dummyValue` does not have to have elements, and it may have objects with\ndifferent properties than the properties in `propertiesToReturn`, but it\nwill be easiest for downstream code if the `dummyValue` conforms to\nthe expected response shape. (Default - No dummy value available)" }, "ignoreErrorOnMissingContext": { - "description": "Ignore an error and return the `dummyValue` instead if the resource was not found.", + "description": "Ignore an error and return the `dummyValue` instead if the resource was not found.\n\n- In case of an `exactIdentifier` lookup, return the `dummyValue` if the resource with\n that identifier was not found.\n- In case of a `propertyMatch` lookup, this setting currently does not have any effect,\n as `propertyMatch` queries can legally return 0 resources.\n\nif `ignoreErrorOnMissingContext` is set, `dummyValue` should be set and be an array.", "default": false, "type": "boolean" }, diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 23d6ca51..519df2fe 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { - "schemaHash": "7c650e458026e83b3de23aef106137c69f10031147873e2638b6d8e078419777", - "revision": 42 + "schemaHash": "5683db246fac20b864d94d7bceef24ebda1a38c8c1f8ef0d5978534097dc9504", + "revision": 43 } \ No newline at end of file diff --git a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts index 0be15f44..cfe040c6 100644 --- a/packages/aws-cdk/lib/context-providers/cc-api-provider.ts +++ b/packages/aws-cdk/lib/context-providers/cc-api-provider.ts @@ -1,10 +1,11 @@ import type { CcApiContextQuery } from '@aws-cdk/cloud-assembly-schema'; +import type { ResourceDescription } from '@aws-sdk/client-cloudcontrol'; +import { ResourceNotFoundException } from '@aws-sdk/client-cloudcontrol'; import { ContextProviderError } from '../../../@aws-cdk/tmp-toolkit-helpers/src/api'; import type { ICloudControlClient } from '../api'; import { type SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import type { ContextProviderPlugin } from '../api/plugin'; import { findJsonValue, getResultObj } from '../util'; -import { ResourceDescription, ResourceNotFoundException } from '@aws-sdk/client-cloudcontrol'; export class CcApiContextProviderPlugin implements ContextProviderPlugin { constructor(private readonly aws: SdkProvider) { @@ -70,7 +71,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { Identifier: exactIdentifier, }); if (!result.ResourceDescription) { - throw new ContextProviderError(`Unexpected CloudControl API behavior: returned empty response`); + throw new ContextProviderError('Unexpected CloudControl API behavior: returned empty response'); } return [foundResourceFromCcApi(result.ResourceDescription)]; @@ -112,7 +113,6 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin { }); }); - return found; } catch (err: any) { if (!(err instanceof ContextProviderError) && !(err instanceof ZeroResourcesFoundError)) { @@ -158,4 +158,4 @@ interface FoundResource { * A specific lookup failure indicating 0 resources found that can be recovered */ class ZeroResourcesFoundError extends Error { -} \ No newline at end of file +} From 9ae446a85ad673bb8e00abd593ed0495ca398e8f Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 24 Mar 2025 19:55:27 +0100 Subject: [PATCH 17/19] Oops, version should be 42 --- packages/@aws-cdk/cloud-assembly-schema/schema/version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 519df2fe..80c8013a 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { "schemaHash": "5683db246fac20b864d94d7bceef24ebda1a38c8c1f8ef0d5978534097dc9504", - "revision": 43 -} \ No newline at end of file + "revision": 42 +} From 195aad18eebd42fdd3713fe7b1bb30c784068193 Mon Sep 17 00:00:00 2001 From: Rico Hermans Date: Tue, 25 Mar 2025 11:04:33 +0100 Subject: [PATCH 18/19] Update version.json From 0d1cd518538dbbe08e2307cf7ba34f45e04ec398 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 25 Mar 2025 11:53:26 +0100 Subject: [PATCH 19/19] Build update --- packages/@aws-cdk/cloud-assembly-schema/schema/version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 80c8013a..935cecd7 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { "schemaHash": "5683db246fac20b864d94d7bceef24ebda1a38c8c1f8ef0d5978534097dc9504", "revision": 42 -} +} \ No newline at end of file