Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ 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.
* To restrict the number of results to return, specify allowEmptyResult or exactResult.
* Either exactIdentifier or propertyMatch should be specified.
* @default - None
*/
Expand All @@ -385,6 +385,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[];

/**
* Whether to allow to return empty result when propertyMatch is specified.
* @default false
*/
readonly allowEmptyResult?: boolean;

/**
* Whether to restrict to return exact one result when propertyMatch is specified.
* @default false
*/
readonly exactResult?: boolean;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.\nIf both exactIdentifier and propertyMatch are specified, then exactIdentifier is used.\nTo restrict the number of results to return, specify allowEmptyResult or exactResult.\nEither exactIdentifier or propertyMatch should be specified. (Default - None)",
"$ref": "#/definitions/Record<string,unknown>"
},
"propertiesToReturn": {
Expand All @@ -1046,6 +1046,16 @@
"type": "string"
}
},
"allowEmptyResult": {
"description": "Whether to allow to return empty result when propertyMatch is specified.",
"default": false,
"type": "boolean"
},
"exactResult": {
"description": "Whether to restrict to return exact one result when propertyMatch is specified.",
"default": false,
"type": "boolean"
},
"account": {
"description": "Query account",
"type": "string"
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/cloud-assembly-schema/schema/version.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"schemaHash": "ba7d47a7a023c39293e99a374af293384eaf1ccd207e515dbdc59dfb5cae4ed6",
"revision": 41
"schemaHash": "9efa7df5c2d110e1877357269e2c499df7840ada8d598738c48314b94979e298",
"revision": 42
}
12 changes: 11 additions & 1 deletion packages/aws-cdk/lib/context-providers/cc-api-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin {
return this.getResource(cc, args.typeName, args.exactIdentifier, args.propertiesToReturn);
} else {
// use listResource
return this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn);
return this.listResources(cc, args.typeName, args.propertyMatch!, args.propertiesToReturn, args.allowEmptyResult, args.exactResult);
}
}

Expand Down Expand Up @@ -85,6 +85,8 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin {
typeName: string,
propertyMatch: Record<string, unknown>,
propertiesToReturn: string[],
allowEmptyResult?: boolean,
exactResult?: boolean,
): Promise<{[key: string]: any}[]> {
const resultObjs: {[key: string]: any}[] = [];

Expand Down Expand Up @@ -122,6 +124,14 @@ export class CcApiContextProviderPlugin implements ContextProviderPlugin {
} catch (err) {
throw new ContextProviderError(`Could not get resources ${JSON.stringify(propertyMatch)}. Error: ${err}`);
}

if (!allowEmptyResult && resultObjs.length === 0) {
throw new ContextProviderError(`Could not find any resources matching ${JSON.stringify(propertyMatch)}`);
}
if (exactResult && resultObjs.length > 1) {
throw new ContextProviderError(`Found ${resultObjs.length} resources matching ${JSON.stringify(propertyMatch)}; please narrow the search criteria`);
}

return resultObjs;
}
}
69 changes: 69 additions & 0 deletions packages/aws-cdk/test/context-providers/cc-api-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,75 @@ test('looks up RDS instance using CC API listResources - error in CC API', async
).rejects.toThrow('Could not get resources {"Endpoint.Port":"5432"}.'); // THEN
});

test('throws an error for empty result', async () => {
// GIVEN
mockCloudControlClient.on(ListResourcesCommand).resolves({
ResourceDescriptions: [
{ Identifier: 'pl-xxxx', Properties: '{"PrefixListName":"name1","PrefixListId":"pl-xxxx","OwnerId":"123456789012"}' },
{ Identifier: 'pl-yyyy', Properties: '{"PrefixListName":"name1","PrefixListId":"pl-yyyy","OwnerId":"234567890123"}' },
{ Identifier: 'pl-zzzz', Properties: '{"PrefixListName":"name2","PrefixListId":"pl-zzzz","OwnerId":"123456789012"}' },
],
});

await expect(
// WHEN
provider.getValue({
account: '123456789012',
region: 'us-east-1',
typeName: 'AWS::EC2::PrefixList',
propertyMatch: { PrefixListName: 'name3' },
propertiesToReturn: ['PrefixListId'],
}),
).rejects.toThrow('Could not find any resources matching {"PrefixListName":"name3"}'); // THEN
});

test('return an empty array for empty result when allowEmptyResult is set', async () => {
// GIVEN
mockCloudControlClient.on(ListResourcesCommand).resolves({
ResourceDescriptions: [
{ Identifier: 'pl-xxxx', Properties: '{"PrefixListName":"name1","PrefixListId":"pl-xxxx","OwnerId":"123456789012"}' },
{ Identifier: 'pl-yyyy', Properties: '{"PrefixListName":"name1","PrefixListId":"pl-yyyy","OwnerId":"234567890123"}' },
{ Identifier: 'pl-zzzz', Properties: '{"PrefixListName":"name2","PrefixListId":"pl-zzzz","OwnerId":"123456789012"}' },
],
});

// WHEN
const results = await provider.getValue({
account: '123456789012',
region: 'us-east-1',
typeName: 'AWS::EC2::PrefixList',
propertyMatch: { PrefixListName: 'name3' },
propertiesToReturn: ['PrefixListId'],
allowEmptyResult: true,
});

// THEN
expect(results.length).toEqual(0);
});

test('throws an error for multiple results when exactResult is set', async () => {
// GIVEN
mockCloudControlClient.on(ListResourcesCommand).resolves({
ResourceDescriptions: [
{ Identifier: 'pl-xxxx', Properties: '{"PrefixListName":"name1","PrefixListId":"pl-xxxx","OwnerId":"123456789012"}' },
{ Identifier: 'pl-yyyy', Properties: '{"PrefixListName":"name1","PrefixListId":"pl-yyyy","OwnerId":"234567890123"}' },
{ Identifier: 'pl-zzzz', Properties: '{"PrefixListName":"name2","PrefixListId":"pl-zzzz","OwnerId":"123456789012"}' },
],
});

await expect(
// WHEN
provider.getValue({
account: '123456789012',
region: 'us-east-1',
typeName: 'AWS::EC2::PrefixList',
propertyMatch: { PrefixListName: 'name1' },
propertiesToReturn: ['PrefixListId'],
exactResult: true,
}),
).rejects.toThrow('Found 2 resources matching {"PrefixListName":"name1"}'); // THEN
});

test('error by specifying both exactIdentifier and propertyMatch', async () => {
// GIVEN
mockCloudControlClient.on(GetResourceCommand).resolves({
Expand Down
Loading