Skip to content

Migrate to AWS SDK V3 - Part 1 #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
16 changes: 14 additions & 2 deletions .github/workflows/integrate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,24 @@ jobs:
run: |
npm update --no-save
npm update --save-dev --no-save
- name: Unit tests
- name: Unit tests (AWS SDK v2)
# Some tests depend on TTY support, which is missing in GA runner
# Workaround taken from https://github.com/actions/runner/issues/241#issuecomment-577360161
run: script -e -c "npm test -- -b"
- name: Packaging tests
env:
SLS_AWS_SDK_V3: '0'
- name: Unit tests (AWS SDK v3)
run: script -e -c "npm test -- -b"
env:
SLS_AWS_SDK_V3: '1'
- name: Packaging tests (AWS SDK v2)
run: npm run integration-test-run-package
env:
SLS_AWS_SDK_V3: '0'
- name: Packaging tests (AWS SDK v3)
run: npm run integration-test-run-package
env:
SLS_AWS_SDK_V3: '1'

windowsNode16:
name: '[Windows] Node 16: Unit tests'
Expand Down
16 changes: 14 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,24 @@ jobs:
then
npx dump-release-notes-from-cc-changelog $NEW_VERSION
fi
- name: Unit tests
- name: Unit tests (AWS SDK v2)
# Some tests depend on TTY support, which is missing in GA runner
# Workaround taken from https://github.com/actions/runner/issues/241#issuecomment-577360161
run: script -e -c "npm test -- -b"
- name: Packaging tests
env:
SLS_AWS_SDK_V3: '0'
- name: Unit tests (AWS SDK v3)
run: script -e -c "npm test -- -b"
env:
SLS_AWS_SDK_V3: '1'
- name: Packaging tests (AWS SDK v2)
run: npm run integration-test-run-package
env:
SLS_AWS_SDK_V3: '0'
- name: Packaging tests (AWS SDK v3)
run: npm run integration-test-run-package
env:
SLS_AWS_SDK_V3: '1'

windowsNode16:
name: '[Windows] Node 16: Unit tests'
Expand Down
76 changes: 76 additions & 0 deletions lib/aws/client-factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use strict';

const { APIGatewayClient } = require('@aws-sdk/client-api-gateway');
const { ApiGatewayV2Client } = require('@aws-sdk/client-apigatewayv2');
const { CloudFormationClient } = require('@aws-sdk/client-cloudformation');
const { CloudWatchClient } = require('@aws-sdk/client-cloudwatch');
const { CloudWatchLogsClient } = require('@aws-sdk/client-cloudwatch-logs');
const { CognitoIdentityProviderClient } = require('@aws-sdk/client-cognito-identity-provider');
const { ECRClient } = require('@aws-sdk/client-ecr');
const { EventBridgeClient } = require('@aws-sdk/client-eventbridge');
const { IAMClient } = require('@aws-sdk/client-iam');
const { LambdaClient } = require('@aws-sdk/client-lambda');
const { S3Client } = require('@aws-sdk/client-s3');
const { SSMClient } = require('@aws-sdk/client-ssm');
const { STSClient } = require('@aws-sdk/client-sts');

// Map service names to their client classes
const CLIENT_MAP = {
APIGateway: APIGatewayClient,
ApiGatewayV2: ApiGatewayV2Client,
CloudFormation: CloudFormationClient,
CloudWatch: CloudWatchClient,
CloudWatchLogs: CloudWatchLogsClient,
CognitoIdentityProvider: CognitoIdentityProviderClient,
ECR: ECRClient,
EventBridge: EventBridgeClient,
IAM: IAMClient,
Lambda: LambdaClient,
S3: S3Client,
SSM: SSMClient,
STS: STSClient,
};

class AWSClientFactory {
constructor(baseConfig = {}) {
this.baseConfig = baseConfig;
this.clients = new Map();
}

/**
* Get a configured AWS service client
* @param {string} serviceName - Name of the AWS service (e.g., 'S3', 'CloudFormation')
* @param {Object} overrideConfig - Configuration to override base config
* @returns {Object} AWS SDK v3 client instance
*/
getClient(serviceName, overrideConfig = {}) {
const ClientClass = CLIENT_MAP[serviceName];
if (!ClientClass) {
throw new Error(`Unknown AWS service: ${serviceName}`);
}

// Create a cache key based on service and config
const configKey = JSON.stringify({ serviceName, ...this.baseConfig, ...overrideConfig });

if (!this.clients.has(configKey)) {
const clientConfig = { ...this.baseConfig, ...overrideConfig };
this.clients.set(configKey, new ClientClass(clientConfig));
}

return this.clients.get(configKey);
}

/**
* Send a command to an AWS service
* @param {string} serviceName - Name of the AWS service
* @param {Object} command - AWS SDK v3 command instance
* @param {Object} clientConfig - Optional client configuration override
* @returns {Promise} Result of the AWS API call
*/
async send(serviceName, command, clientConfig = {}) {
const client = this.getClient(serviceName, clientConfig);
return client.send(command);
}
}

module.exports = AWSClientFactory;
259 changes: 259 additions & 0 deletions lib/aws/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
'use strict';

// API Gateway Commands
const {
GetAccountCommand,
UpdateAccountCommand,
GetApiKeyCommand,
CreateStageCommand,
GetUsagePlansCommand,
UpdateUsagePlanCommand,
TagResourceCommand,
UntagResourceCommand,
UpdateStageCommand,
} = require('@aws-sdk/client-api-gateway');

// API Gateway V2 Commands
const { GetApiCommand } = require('@aws-sdk/client-apigatewayv2');

// CloudFormation Commands
const {
CreateStackCommand,
CreateChangeSetCommand,
DeleteChangeSetCommand,
ExecuteChangeSetCommand,
UpdateStackCommand,
DeleteStackCommand,
DescribeStacksCommand,
ValidateTemplateCommand,
SetStackPolicyCommand,
GetTemplateCommand,
ListStackResourcesCommand,
DescribeStackResourceCommand,
DescribeStackEventsCommand,
ListExportsCommand,
} = require('@aws-sdk/client-cloudformation');

// CloudWatch Commands
const { GetMetricStatisticsCommand } = require('@aws-sdk/client-cloudwatch');

// CloudWatch Logs Commands
const {
DescribeLogStreamsCommand,
FilterLogEventsCommand,
DeleteSubscriptionFilterCommand,
} = require('@aws-sdk/client-cloudwatch-logs');

// Cognito Identity Provider Commands
const {
ListUserPoolsCommand,
DescribeUserPoolCommand,
UpdateUserPoolCommand,
} = require('@aws-sdk/client-cognito-identity-provider');

// ECR Commands
const {
DeleteRepositoryCommand,
DescribeRepositoriesCommand,
GetAuthorizationTokenCommand,
CreateRepositoryCommand,
DescribeImagesCommand,
} = require('@aws-sdk/client-ecr');

// EventBridge Commands
const {
CreateEventBusCommand,
DeleteEventBusCommand,
PutRuleCommand,
DeleteRuleCommand,
PutTargetsCommand,
RemoveTargetsCommand,
} = require('@aws-sdk/client-eventbridge');

// IAM Commands
const {
GetRoleCommand,
ListAttachedRolePoliciesCommand,
CreateRoleCommand,
AttachRolePolicyCommand,
} = require('@aws-sdk/client-iam');

// Lambda Commands
const {
GetFunctionCommand,
UpdateFunctionConfigurationCommand,
UpdateFunctionCodeCommand,
InvokeCommand,
ListVersionsByFunctionCommand,
GetLayerVersionCommand,
AddPermissionCommand,
RemovePermissionCommand,
} = require('@aws-sdk/client-lambda');

// S3 Commands
const {
ListObjectsV2Command,
ListObjectVersionsCommand,
DeleteObjectsCommand,
HeadObjectCommand,
PutObjectCommand,
GetObjectCommand,
GetBucketLocationCommand,
HeadBucketCommand,
GetBucketNotificationConfigurationCommand,
PutBucketNotificationConfigurationCommand,
} = require('@aws-sdk/client-s3');

// SSM Commands
const { GetParameterCommand } = require('@aws-sdk/client-ssm');

// STS Commands
const { GetCallerIdentityCommand } = require('@aws-sdk/client-sts');

/**
* Map v2 method names to v3 command classes
* Format: { ServiceName: { methodName: CommandClass } }
*/
const COMMAND_MAP = {
APIGateway: {
getAccount: GetAccountCommand,
updateAccount: UpdateAccountCommand,
getApiKey: GetApiKeyCommand,
createStage: CreateStageCommand,
getUsagePlans: GetUsagePlansCommand,
updateUsagePlan: UpdateUsagePlanCommand,
tagResource: TagResourceCommand,
untagResource: UntagResourceCommand,
updateStage: UpdateStageCommand,
},

ApiGatewayV2: {
getApi: GetApiCommand,
},

CloudFormation: {
createStack: CreateStackCommand,
createChangeSet: CreateChangeSetCommand,
deleteChangeSet: DeleteChangeSetCommand,
executeChangeSet: ExecuteChangeSetCommand,
updateStack: UpdateStackCommand,
deleteStack: DeleteStackCommand,
describeStacks: DescribeStacksCommand,
validateTemplate: ValidateTemplateCommand,
setStackPolicy: SetStackPolicyCommand,
getTemplate: GetTemplateCommand,
listStackResources: ListStackResourcesCommand,
describeStackResource: DescribeStackResourceCommand,
describeStackEvents: DescribeStackEventsCommand,
listExports: ListExportsCommand,
},

CloudWatch: {
getMetricStatistics: GetMetricStatisticsCommand,
},

CloudWatchLogs: {
describeLogStreams: DescribeLogStreamsCommand,
filterLogEvents: FilterLogEventsCommand,
deleteSubscriptionFilter: DeleteSubscriptionFilterCommand,
},

CognitoIdentityProvider: {
listUserPools: ListUserPoolsCommand,
describeUserPool: DescribeUserPoolCommand,
updateUserPool: UpdateUserPoolCommand,
},

ECR: {
deleteRepository: DeleteRepositoryCommand,
describeRepositories: DescribeRepositoriesCommand,
getAuthorizationToken: GetAuthorizationTokenCommand,
createRepository: CreateRepositoryCommand,
describeImages: DescribeImagesCommand,
},

EventBridge: {
createEventBus: CreateEventBusCommand,
deleteEventBus: DeleteEventBusCommand,
putRule: PutRuleCommand,
deleteRule: DeleteRuleCommand,
putTargets: PutTargetsCommand,
removeTargets: RemoveTargetsCommand,
},

IAM: {
getRole: GetRoleCommand,
listAttachedRolePolicies: ListAttachedRolePoliciesCommand,
createRole: CreateRoleCommand,
attachRolePolicy: AttachRolePolicyCommand,
},

Lambda: {
getFunction: GetFunctionCommand,
updateFunctionConfiguration: UpdateFunctionConfigurationCommand,
updateFunctionCode: UpdateFunctionCodeCommand,
invoke: InvokeCommand,
listVersionsByFunction: ListVersionsByFunctionCommand,
getLayerVersion: GetLayerVersionCommand,
addPermission: AddPermissionCommand,
removePermission: RemovePermissionCommand,
},

S3: {
listObjectsV2: ListObjectsV2Command,
listObjectVersions: ListObjectVersionsCommand,
deleteObjects: DeleteObjectsCommand,
headObject: HeadObjectCommand,
putObject: PutObjectCommand,
getObject: GetObjectCommand,
getBucketLocation: GetBucketLocationCommand,
headBucket: HeadBucketCommand,
getBucketNotificationConfiguration: GetBucketNotificationConfigurationCommand,
putBucketNotificationConfiguration: PutBucketNotificationConfigurationCommand,
// Note: upload is handled separately as it's not a direct API call
},

SSM: {
getParameter: GetParameterCommand,
},

STS: {
getCallerIdentity: GetCallerIdentityCommand,
},
};

/**
* Get command class for a service method
* @param {string} serviceName - AWS service name
* @param {string} methodName - Method name from v2 SDK
* @returns {Function} Command class constructor
*/
function getCommand(serviceName, methodName) {
const serviceCommands = COMMAND_MAP[serviceName];
if (!serviceCommands) {
throw new Error(`Unknown AWS service: ${serviceName}`);
}

const CommandClass = serviceCommands[methodName];
if (!CommandClass) {
throw new Error(`Unknown method '${methodName}' for service '${serviceName}'`);
}

return CommandClass;
}

/**
* Create a command instance for a service method
* @param {string} serviceName - AWS service name
* @param {string} methodName - Method name from v2 SDK
* @param {Object} params - Parameters for the command
* @returns {Object} Command instance
*/
function createCommand(serviceName, methodName, params = {}) {
const CommandClass = getCommand(serviceName, methodName);
return new CommandClass(params);
}

module.exports = {
createCommand,
};
Loading