Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,7 @@ const toolkitLib = configureProject(
sdkDepForLib('@aws-sdk/client-iam'),
sdkDepForLib('@aws-sdk/client-kms'),
sdkDepForLib('@aws-sdk/client-lambda'),
'@aws-sdk/client-quicksight@3.953.0', // Pin to match other SDK clients and avoid @smithy/types version mismatch
sdkDepForLib('@aws-sdk/client-route-53'),
sdkDepForLib('@aws-sdk/client-s3'),
sdkDepForLib('@aws-sdk/client-secrets-manager'),
Expand Down
3,779 changes: 3,656 additions & 123 deletions packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions packages/@aws-cdk/toolkit-lib/.projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/api/aws-auth/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,24 @@ import {
type UpdateFunctionConfigurationCommandOutput,
waitUntilFunctionUpdatedV2,
} from '@aws-sdk/client-lambda';
import {
QuickSightClient,
UpdateAnalysisCommand,
type UpdateAnalysisCommandInput,
type UpdateAnalysisCommandOutput,
UpdateDashboardCommand,
type UpdateDashboardCommandInput,
type UpdateDashboardCommandOutput,
UpdateDataSetCommand,
type UpdateDataSetCommandInput,
type UpdateDataSetCommandOutput,
UpdateDataSourceCommand,
type UpdateDataSourceCommandInput,
type UpdateDataSourceCommandOutput,
UpdateTemplateCommand,
type UpdateTemplateCommandInput,
type UpdateTemplateCommandOutput,
} from '@aws-sdk/client-quicksight';
import {
GetHostedZoneCommand,
type GetHostedZoneCommandInput,
Expand Down Expand Up @@ -583,6 +601,14 @@ export interface IRoute53Client {
listHostedZonesByName(input: ListHostedZonesByNameCommandInput): Promise<ListHostedZonesByNameCommandOutput>;
}

export interface IQuickSightClient {
updateAnalysis(input: UpdateAnalysisCommandInput): Promise<UpdateAnalysisCommandOutput>;
updateDashboard(input: UpdateDashboardCommandInput): Promise<UpdateDashboardCommandOutput>;
updateDataSet(input: UpdateDataSetCommandInput): Promise<UpdateDataSetCommandOutput>;
updateDataSource(input: UpdateDataSourceCommandInput): Promise<UpdateDataSourceCommandOutput>;
updateTemplate(input: UpdateTemplateCommandInput): Promise<UpdateTemplateCommandOutput>;
}

export interface IS3Client {
deleteObjects(input: DeleteObjectsCommandInput): Promise<DeleteObjectsCommandOutput>;
deleteObjectTagging(input: DeleteObjectTaggingCommandInput): Promise<DeleteObjectTaggingCommandOutput>;
Expand Down Expand Up @@ -1042,6 +1068,22 @@ export class SDK {
};
}

public quickSight(): IQuickSightClient {
const client = new QuickSightClient(this.config);
return {
updateAnalysis: (input: UpdateAnalysisCommandInput): Promise<UpdateAnalysisCommandOutput> =>
client.send(new UpdateAnalysisCommand(input)),
updateDashboard: (input: UpdateDashboardCommandInput): Promise<UpdateDashboardCommandOutput> =>
client.send(new UpdateDashboardCommand(input)),
updateDataSet: (input: UpdateDataSetCommandInput): Promise<UpdateDataSetCommandOutput> =>
client.send(new UpdateDataSetCommand(input)),
updateDataSource: (input: UpdateDataSourceCommandInput): Promise<UpdateDataSourceCommandOutput> =>
client.send(new UpdateDataSourceCommand(input)),
updateTemplate: (input: UpdateTemplateCommandInput): Promise<UpdateTemplateCommandOutput> =>
client.send(new UpdateTemplateCommand(input)),
};
}

public s3(): IS3Client {
const client = new S3Client(this.config);
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from './common';
import { isHotswappableEcsServiceChange } from './ecs-services';
import { isHotswappableLambdaFunctionChange } from './lambda-functions';
import { isHotswappableQuickSightChange } from './quicksight';
import {
skipChangeForS3DeployCustomResourcePolicy,
isHotswappableS3BucketDeploymentChange,
Expand Down Expand Up @@ -60,6 +61,13 @@ const RESOURCE_DETECTORS: { [key: string]: HotswapDetector } = {
'AWS::AppSync::GraphQLSchema': isHotswappableAppSyncChange,
'AWS::AppSync::ApiKey': isHotswappableAppSyncChange,

// QuickSight
'AWS::QuickSight::DataSet': isHotswappableQuickSightChange,
'AWS::QuickSight::DataSource': isHotswappableQuickSightChange,
'AWS::QuickSight::Dashboard': isHotswappableQuickSightChange,
'AWS::QuickSight::Analysis': isHotswappableQuickSightChange,
'AWS::QuickSight::Template': isHotswappableQuickSightChange,

'AWS::BedrockAgentCore::Runtime': isHotswappableBedrockAgentCoreRuntimeChange,
'AWS::ECS::TaskDefinition': isHotswappableEcsServiceChange,
'AWS::CodeBuild::Project': isHotswappableCodeBuildProjectChange,
Expand Down
128 changes: 128 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/api/hotswap/quicksight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { type HotswapChange, classifyChanges } from './common';
import type { ResourceChange } from '../../payloads/hotswap';
import type { SDK } from '../aws-auth/private';
import type { EvaluateCloudFormationTemplate } from '../cloudformation';

const QUICKSIGHT_RESOURCE_TYPES: Record<string, { hotswappableProps: string[]; service: string }> = {
'AWS::QuickSight::DataSet': { hotswappableProps: ['PhysicalTableMap', 'LogicalTableMap', 'Name'], service: 'quicksight-dataset' },
'AWS::QuickSight::DataSource': { hotswappableProps: ['DataSourceParameters', 'Name'], service: 'quicksight-datasource' },
'AWS::QuickSight::Dashboard': { hotswappableProps: ['Definition', 'Name', 'SourceEntity'], service: 'quicksight-dashboard' },
'AWS::QuickSight::Analysis': { hotswappableProps: ['Definition', 'Name', 'SourceEntity'], service: 'quicksight-analysis' },
'AWS::QuickSight::Template': { hotswappableProps: ['Definition', 'Name', 'SourceEntity'], service: 'quicksight-template' },
};

export async function isHotswappableQuickSightChange(
logicalId: string,
change: ResourceChange,
evaluateCfnTemplate: EvaluateCloudFormationTemplate,
): Promise<HotswapChange[]> {
const resourceConfig = QUICKSIGHT_RESOURCE_TYPES[change.newValue.Type];
if (!resourceConfig) {
return [];
}

const ret: HotswapChange[] = [];
const classifiedChanges = classifyChanges(change, resourceConfig.hotswappableProps);
classifiedChanges.reportNonHotswappablePropertyChanges(ret);

if (classifiedChanges.namesOfHotswappableProps.length === 0) {
return ret;
}

const awsAccountId = change.newValue.Properties?.AwsAccountId
? await evaluateCfnTemplate.evaluateCfnExpression(change.newValue.Properties.AwsAccountId)
: await evaluateCfnTemplate.evaluateCfnExpression({ Ref: 'AWS::AccountId' });

const resourceId = await evaluateCfnTemplate.establishResourcePhysicalName(
logicalId,
change.newValue.Properties?.DataSetId ??
change.newValue.Properties?.DataSourceId ??
change.newValue.Properties?.DashboardId ??
change.newValue.Properties?.AnalysisId ??
change.newValue.Properties?.TemplateId,
);

if (!resourceId) {
return ret;
}

ret.push({
change: {
cause: change,
resources: [{
logicalId,
resourceType: change.newValue.Type,
physicalName: resourceId,
metadata: evaluateCfnTemplate.metadataFor(logicalId),
}],
},
hotswappable: true,
service: resourceConfig.service,
apply: async (sdk: SDK) => {
const props = change.newValue.Properties ?? {};
const evaluatedProps: Record<string, any> = {};

for (const propName of classifiedChanges.namesOfHotswappableProps) {
if (props[propName] !== undefined) {
evaluatedProps[propName] = await evaluateCfnTemplate.evaluateCfnExpression(props[propName]);
}
}

const evaluatedName = props.Name !== undefined
? (evaluatedProps.Name ?? await evaluateCfnTemplate.evaluateCfnExpression(props.Name))
: undefined;

switch (change.newValue.Type) {
case 'AWS::QuickSight::DataSet':
await sdk.quickSight().updateDataSet({
AwsAccountId: awsAccountId,
DataSetId: resourceId,
Name: evaluatedName,
PhysicalTableMap: evaluatedProps.PhysicalTableMap,
LogicalTableMap: evaluatedProps.LogicalTableMap,
ImportMode: props.ImportMode !== undefined
? await evaluateCfnTemplate.evaluateCfnExpression(props.ImportMode)
: undefined,
});
break;
case 'AWS::QuickSight::DataSource':
await sdk.quickSight().updateDataSource({
AwsAccountId: awsAccountId,
DataSourceId: resourceId,
Name: evaluatedName,
DataSourceParameters: evaluatedProps.DataSourceParameters,
});
break;
case 'AWS::QuickSight::Dashboard':
await sdk.quickSight().updateDashboard({
AwsAccountId: awsAccountId,
DashboardId: resourceId,
Name: evaluatedName,
Definition: evaluatedProps.Definition,
SourceEntity: evaluatedProps.SourceEntity,
});
break;
case 'AWS::QuickSight::Analysis':
await sdk.quickSight().updateAnalysis({
AwsAccountId: awsAccountId,
AnalysisId: resourceId,
Name: evaluatedName,
Definition: evaluatedProps.Definition,
SourceEntity: evaluatedProps.SourceEntity,
});
break;
case 'AWS::QuickSight::Template':
await sdk.quickSight().updateTemplate({
AwsAccountId: awsAccountId,
TemplateId: resourceId,
Name: evaluatedName,
Definition: evaluatedProps.Definition,
SourceEntity: evaluatedProps.SourceEntity,
});
break;
}
},
});

return ret;
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/toolkit-lib/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/@aws-cdk/toolkit-lib/test/_helpers/mock-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ElasticLoadBalancingV2Client } from '@aws-sdk/client-elastic-load-balan
import { IAMClient } from '@aws-sdk/client-iam';
import { KMSClient } from '@aws-sdk/client-kms';
import { LambdaClient } from '@aws-sdk/client-lambda';
import { QuickSightClient } from '@aws-sdk/client-quicksight';
import { Route53Client } from '@aws-sdk/client-route-53';
import { S3Client } from '@aws-sdk/client-s3';
import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
Expand Down Expand Up @@ -51,6 +52,7 @@ export const awsMock = {
iAM: mockClient(IAMClient),
kMS: mockClient(KMSClient),
lambda: mockClient(LambdaClient),
quickSight: mockClient(QuickSightClient),
route53: mockClient(Route53Client),
s3: mockClient(S3Client),
sSM: mockClient(SSMClient),
Expand All @@ -73,6 +75,7 @@ export const mockElasticLoadBalancingV2Client = awsMock.elasticLoadBalancingV2;
export const mockIAMClient = awsMock.iAM;
export const mockKMSClient = awsMock.kMS;
export const mockLambdaClient = awsMock.lambda;
export const mockQuickSightClient = awsMock.quickSight;
export const mockRoute53Client = awsMock.route53;
export const mockS3Client = awsMock.s3;
export const mockSSMClient = awsMock.sSM;
Expand Down
Loading
Loading