Skip to content

Commit 19e8e1c

Browse files
refactor(redshift): migrate to aws-sdk v3 (aws#8069)
## Problem AWS SDK V2 is at EOL. ## Solution Migrate Redshift client to V3 --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: invictus <[email protected]>
1 parent ac44c5e commit 19e8e1c

File tree

10 files changed

+1806
-223
lines changed

10 files changed

+1806
-223
lines changed

package-lock.json

Lines changed: 1572 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,9 @@
584584
"@aws-sdk/client-iot": "~3.693.0",
585585
"@aws-sdk/client-iotsecuretunneling": "~3.693.0",
586586
"@aws-sdk/client-lambda": "<3.731.0",
587+
"@aws-sdk/client-redshift": "~3.693.0",
588+
"@aws-sdk/client-redshift-data": "~3.693.0",
589+
"@aws-sdk/client-redshift-serverless": "~3.693.0",
587590
"@aws-sdk/client-s3": "<3.731.0",
588591
"@aws-sdk/client-s3-control": "^3.830.0",
589592
"@aws-sdk/client-sagemaker": "<3.696.0",

packages/core/src/awsService/redshift/notebook/redshiftNotebookController.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import * as vscode from 'vscode'
88
import { DefaultRedshiftClient } from '../../../shared/clients/redshiftClient'
99
import { ConnectionParams } from '../models/models'
10-
import { RedshiftData } from 'aws-sdk'
10+
import { ColumnMetadata, Field } from '@aws-sdk/client-redshift-data'
1111
import { telemetry } from '../../../shared/telemetry/telemetry'
1212

1313
export class RedshiftNotebookController {
@@ -79,8 +79,8 @@ export class RedshiftNotebookController {
7979
}
8080

8181
let executionId: string | undefined
82-
let columnMetadata: RedshiftData.ColumnMetadataList | undefined
83-
const records: RedshiftData.SqlRecords = []
82+
let columnMetadata: ColumnMetadata[] | undefined
83+
const records: Field[][] = []
8484
let nextToken: string | undefined
8585
// get all the pages of the result
8686
do {
@@ -90,7 +90,7 @@ export class RedshiftNotebookController {
9090
nextToken,
9191
executionId
9292
)
93-
if (result) {
93+
if (result && result.statementResultResponse.Records) {
9494
nextToken = result.statementResultResponse.NextToken
9595
executionId = result.executionId
9696
columnMetadata = result.statementResultResponse.ColumnMetadata
@@ -116,7 +116,7 @@ export class RedshiftNotebookController {
116116
})
117117
}
118118

119-
public getAsTable(connectionParams: ConnectionParams, columns: string[], records: RedshiftData.SqlRecords) {
119+
public getAsTable(connectionParams: ConnectionParams, columns: string[], records: Field[][]) {
120120
if (!records || records.length === 0) {
121121
return '<p>No records to display<p>'
122122
}

packages/core/src/shared/clients/redshiftClient.ts

Lines changed: 76 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,43 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import { Redshift, RedshiftServerless, RedshiftData } from 'aws-sdk'
8-
import globals from '../extensionGlobals'
9-
import { ClusterCredentials, ClustersMessage, GetClusterCredentialsMessage } from 'aws-sdk/clients/redshift'
107
import {
11-
GetCredentialsRequest,
12-
GetCredentialsResponse,
13-
ListWorkgroupsResponse,
14-
} from 'aws-sdk/clients/redshiftserverless'
8+
ClusterCredentials,
9+
ClustersMessage,
10+
DescribeClustersCommand,
11+
DescribeClustersMessage,
12+
GetClusterCredentialsCommand,
13+
GetClusterCredentialsMessage,
14+
RedshiftClient,
15+
} from '@aws-sdk/client-redshift'
1516
import {
17+
DescribeStatementCommand,
1618
DescribeStatementRequest,
19+
ExecuteStatementCommand,
20+
GetStatementResultCommand,
1721
GetStatementResultRequest,
1822
GetStatementResultResponse,
23+
ListDatabasesCommand,
24+
ListDatabasesRequest,
1925
ListDatabasesResponse,
26+
ListSchemasCommand,
27+
ListSchemasRequest,
2028
ListSchemasResponse,
29+
ListTablesCommand,
30+
ListTablesRequest,
2131
ListTablesResponse,
22-
} from 'aws-sdk/clients/redshiftdata'
32+
RedshiftDataClient,
33+
} from '@aws-sdk/client-redshift-data'
34+
import {
35+
GetCredentialsCommand,
36+
GetCredentialsRequest,
37+
GetCredentialsResponse,
38+
ListWorkgroupsCommand,
39+
ListWorkgroupsRequest,
40+
ListWorkgroupsResponse,
41+
RedshiftServerlessClient,
42+
} from '@aws-sdk/client-redshift-serverless'
43+
import globals from '../extensionGlobals'
2344
import { ConnectionParams, ConnectionType, RedshiftWarehouseType } from '../../awsService/redshift/models/models'
2445
import { sleep } from '../utilities/timeoutUtils'
2546
import { SecretsManagerClient } from './secretsManagerClient'
@@ -37,21 +58,21 @@ export class DefaultRedshiftClient {
3758
public readonly regionCode: string,
3859
private readonly redshiftDataClientProvider: (
3960
regionCode: string
40-
) => Promise<RedshiftData> = createRedshiftDataClient,
41-
private readonly redshiftClientProvider: (regionCode: string) => Promise<Redshift> = createRedshiftSdkClient,
61+
) => RedshiftDataClient = createRedshiftDataClient,
62+
private readonly redshiftClientProvider: (regionCode: string) => RedshiftClient = createRedshiftSdkClient,
4263
private readonly redshiftServerlessClientProvider: (
4364
regionCode: string
44-
) => Promise<RedshiftServerless> = createRedshiftServerlessSdkClient
65+
) => RedshiftServerlessClient = createRedshiftServerlessSdkClient
4566
) {}
4667

4768
// eslint-disable-next-line require-yield
4869
public async describeProvisionedClusters(nextToken?: string): Promise<ClustersMessage> {
49-
const redshiftClient = await this.redshiftClientProvider(this.regionCode)
50-
const request: Redshift.DescribeClustersMessage = {
70+
const redshiftClient = this.redshiftClientProvider(this.regionCode)
71+
const request: DescribeClustersMessage = {
5172
Marker: nextToken,
5273
MaxRecords: 20,
5374
}
54-
const response = await redshiftClient.describeClusters(request).promise()
75+
const response = await redshiftClient.send(new DescribeClustersCommand(request))
5576
if (response.Clusters) {
5677
response.Clusters = response.Clusters.filter(
5778
(cluster) => cluster.ClusterAvailabilityStatus?.toLowerCase() === 'available'
@@ -61,12 +82,12 @@ export class DefaultRedshiftClient {
6182
}
6283

6384
public async listServerlessWorkgroups(nextToken?: string): Promise<ListWorkgroupsResponse> {
64-
const redshiftServerlessClient = await this.redshiftServerlessClientProvider(this.regionCode)
65-
const request: RedshiftServerless.ListWorkgroupsRequest = {
85+
const redshiftServerlessClient = this.redshiftServerlessClientProvider(this.regionCode)
86+
const request: ListWorkgroupsRequest = {
6687
nextToken: nextToken,
6788
maxResults: 20,
6889
}
69-
const response = await redshiftServerlessClient.listWorkgroups(request).promise()
90+
const response = await redshiftServerlessClient.send(new ListWorkgroupsCommand(request))
7091
if (response.workgroups) {
7192
response.workgroups = response.workgroups.filter(
7293
(workgroup) => workgroup.status?.toLowerCase() === 'available'
@@ -76,10 +97,10 @@ export class DefaultRedshiftClient {
7697
}
7798

7899
public async listDatabases(connectionParams: ConnectionParams, nextToken?: string): Promise<ListDatabasesResponse> {
79-
const redshiftDataClient = await this.redshiftDataClientProvider(this.regionCode)
100+
const redshiftDataClient = this.redshiftDataClientProvider(this.regionCode)
80101
const warehouseType = connectionParams.warehouseType
81102
const warehouseIdentifier = connectionParams.warehouseIdentifier
82-
const input: RedshiftData.ListDatabasesRequest = {
103+
const input: ListDatabasesRequest = {
83104
ClusterIdentifier: warehouseType === RedshiftWarehouseType.PROVISIONED ? warehouseIdentifier : undefined,
84105
Database: connectionParams.database,
85106
DbUser:
@@ -94,13 +115,13 @@ export class DefaultRedshiftClient {
94115
? connectionParams.secret
95116
: undefined,
96117
}
97-
return redshiftDataClient.listDatabases(input).promise()
118+
return redshiftDataClient.send(new ListDatabasesCommand(input))
98119
}
99120
public async listSchemas(connectionParams: ConnectionParams, nextToken?: string): Promise<ListSchemasResponse> {
100-
const redshiftDataClient = await this.redshiftDataClientProvider(this.regionCode)
121+
const redshiftDataClient = this.redshiftDataClientProvider(this.regionCode)
101122
const warehouseType = connectionParams.warehouseType
102123
const warehouseIdentifier = connectionParams.warehouseIdentifier
103-
const input: RedshiftData.ListSchemasRequest = {
124+
const input: ListSchemasRequest = {
104125
ClusterIdentifier: warehouseType === RedshiftWarehouseType.PROVISIONED ? warehouseIdentifier : undefined,
105126
Database: connectionParams.database,
106127
DbUser:
@@ -114,18 +135,18 @@ export class DefaultRedshiftClient {
114135
? connectionParams.secret
115136
: undefined,
116137
}
117-
return redshiftDataClient.listSchemas(input).promise()
138+
return redshiftDataClient.send(new ListSchemasCommand(input))
118139
}
119140

120141
public async listTables(
121142
connectionParams: ConnectionParams,
122143
schemaName: string,
123144
nextToken?: string
124145
): Promise<ListTablesResponse> {
125-
const redshiftDataClient = await this.redshiftDataClientProvider(this.regionCode)
146+
const redshiftDataClient = this.redshiftDataClientProvider(this.regionCode)
126147
const warehouseType = connectionParams.warehouseType
127148
const warehouseIdentifier = connectionParams.warehouseIdentifier
128-
const input: RedshiftData.ListTablesRequest = {
149+
const input: ListTablesRequest = {
129150
ClusterIdentifier: warehouseType === RedshiftWarehouseType.PROVISIONED ? warehouseIdentifier : undefined,
130151
DbUser:
131152
connectionParams.username && connectionParams.connectionType !== ConnectionType.DatabaseUser
@@ -140,7 +161,7 @@ export class DefaultRedshiftClient {
140161
? connectionParams.secret
141162
: undefined,
142163
}
143-
const ListTablesResponse = redshiftDataClient.listTables(input).promise()
164+
const ListTablesResponse = redshiftDataClient.send(new ListTablesCommand(input))
144165
return ListTablesResponse
145166
}
146167

@@ -150,11 +171,11 @@ export class DefaultRedshiftClient {
150171
nextToken?: string,
151172
executionId?: string
152173
): Promise<ExecuteQueryResponse | undefined> {
153-
const redshiftData = await this.redshiftDataClientProvider(this.regionCode)
174+
const redshiftData = this.redshiftDataClientProvider(this.regionCode)
154175
// if executionId is not passed in, that means that we're executing and retrieving the results of the query for the first time.
155176
if (!executionId) {
156-
const execution = await redshiftData
157-
.executeStatement({
177+
const execution = await redshiftData.send(
178+
new ExecuteStatementCommand({
158179
ClusterIdentifier:
159180
connectionParams.warehouseType === RedshiftWarehouseType.PROVISIONED
160181
? connectionParams.warehouseIdentifier
@@ -174,15 +195,15 @@ export class DefaultRedshiftClient {
174195
? connectionParams.secret
175196
: undefined,
176197
})
177-
.promise()
198+
)
178199

179200
executionId = execution.Id
180201
type Status = 'RUNNING' | 'FAILED' | 'FINISHED'
181202
let status: Status = 'RUNNING'
182203
while (status === 'RUNNING') {
183-
const describeStatementResponse = await redshiftData
184-
.describeStatement({ Id: executionId } as DescribeStatementRequest)
185-
.promise()
204+
const describeStatementResponse = await redshiftData.send(
205+
new DescribeStatementCommand({ Id: executionId } as DescribeStatementRequest)
206+
)
186207
if (describeStatementResponse.Status === 'FAILED' || describeStatementResponse.Status === 'FINISHED') {
187208
status = describeStatementResponse.Status
188209
if (status === 'FAILED') {
@@ -198,9 +219,9 @@ export class DefaultRedshiftClient {
198219
}
199220
}
200221
}
201-
const result = await redshiftData
202-
.getStatementResult({ Id: executionId, NextToken: nextToken } as GetStatementResultRequest)
203-
.promise()
222+
const result = await redshiftData.send(
223+
new GetStatementResultCommand({ Id: executionId, NextToken: nextToken } as GetStatementResultRequest)
224+
)
204225

205226
return { statementResultResponse: result, executionId: executionId } as ExecuteQueryResponse
206227
}
@@ -210,20 +231,20 @@ export class DefaultRedshiftClient {
210231
connectionParams: ConnectionParams
211232
): Promise<ClusterCredentials | GetCredentialsResponse> {
212233
if (warehouseType === RedshiftWarehouseType.PROVISIONED) {
213-
const redshiftClient = await this.redshiftClientProvider(this.regionCode)
234+
const redshiftClient = this.redshiftClientProvider(this.regionCode)
214235
const getClusterCredentialsRequest: GetClusterCredentialsMessage = {
215236
DbUser: connectionParams.username!,
216237
DbName: connectionParams.database,
217238
ClusterIdentifier: connectionParams.warehouseIdentifier,
218239
}
219-
return redshiftClient.getClusterCredentials(getClusterCredentialsRequest).promise()
240+
return redshiftClient.send(new GetClusterCredentialsCommand(getClusterCredentialsRequest))
220241
} else {
221-
const redshiftServerless = await this.redshiftServerlessClientProvider(this.regionCode)
242+
const redshiftServerless = this.redshiftServerlessClientProvider(this.regionCode)
222243
const getCredentialsRequest: GetCredentialsRequest = {
223244
dbName: connectionParams.database,
224245
workgroupName: connectionParams.warehouseIdentifier,
225246
}
226-
return redshiftServerless.getCredentials(getCredentialsRequest).promise()
247+
return redshiftServerless.send(new GetCredentialsCommand(getCredentialsRequest))
227248
}
228249
}
229250
public genUniqueId(connectionParams: ConnectionParams): string {
@@ -258,13 +279,22 @@ export class DefaultRedshiftClient {
258279
}
259280
}
260281

261-
async function createRedshiftSdkClient(regionCode: string): Promise<Redshift> {
262-
return await globals.sdkClientBuilder.createAwsService(Redshift, { computeChecksums: true }, regionCode)
282+
function createRedshiftSdkClient(regionCode: string): RedshiftClient {
283+
return globals.sdkClientBuilderV3.createAwsService({
284+
serviceClient: RedshiftClient,
285+
clientOptions: { region: regionCode },
286+
})
263287
}
264288

265-
async function createRedshiftServerlessSdkClient(regionCode: string): Promise<RedshiftServerless> {
266-
return await globals.sdkClientBuilder.createAwsService(RedshiftServerless, { computeChecksums: true }, regionCode)
289+
function createRedshiftServerlessSdkClient(regionCode: string): RedshiftServerlessClient {
290+
return globals.sdkClientBuilderV3.createAwsService({
291+
serviceClient: RedshiftServerlessClient,
292+
clientOptions: { region: regionCode },
293+
})
267294
}
268-
async function createRedshiftDataClient(regionCode: string): Promise<RedshiftData> {
269-
return await globals.sdkClientBuilder.createAwsService(RedshiftData, { computeChecksums: true }, regionCode)
295+
function createRedshiftDataClient(regionCode: string): RedshiftDataClient {
296+
return globals.sdkClientBuilderV3.createAwsService({
297+
serviceClient: RedshiftDataClient,
298+
clientOptions: { region: regionCode },
299+
})
270300
}

packages/core/src/test/awsService/redshift/explorer/redshiftDatabaseNode.test.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import sinon = require('sinon')
6+
import { mockClient } from 'aws-sdk-client-mock'
77
import { RedshiftDatabaseNode } from '../../../../awsService/redshift/explorer/redshiftDatabaseNode'
8-
import { RedshiftData } from 'aws-sdk'
8+
import { RedshiftDataClient, ListSchemasCommand } from '@aws-sdk/client-redshift-data'
99
import { DefaultRedshiftClient } from '../../../../shared/clients/redshiftClient'
1010
import { ConnectionParams, ConnectionType, RedshiftWarehouseType } from '../../../../awsService/redshift/models/models'
1111
import assert = require('assert')
@@ -14,44 +14,37 @@ import { AWSTreeNodeBase } from '../../../../shared/treeview/nodes/awsTreeNodeBa
1414
import { MoreResultsNode } from '../../../../awsexplorer/moreResultsNode'
1515

1616
describe('RedshiftDatabaseNode', function () {
17-
const sandbox = sinon.createSandbox()
18-
const mockRedshiftData = <RedshiftData>{}
19-
const redshiftClient = new DefaultRedshiftClient('us-east-1', async () => mockRedshiftData, undefined, undefined)
17+
const mockRedshiftData = mockClient(RedshiftDataClient)
18+
const redshiftClient = new DefaultRedshiftClient('us-east-1', () => mockRedshiftData as any, undefined, undefined)
2019
const connectionParams = new ConnectionParams(
2120
ConnectionType.TempCreds,
2221
'testDb1',
2322
'warehouseId',
2423
RedshiftWarehouseType.PROVISIONED
2524
)
26-
let listSchemasStub: sinon.SinonStub
2725

2826
describe('getChildren', function () {
29-
beforeEach(function () {
30-
listSchemasStub = sandbox.stub()
31-
mockRedshiftData.listSchemas = listSchemasStub
32-
})
33-
3427
afterEach(function () {
35-
sandbox.reset()
28+
mockRedshiftData.reset()
3629
})
3730

3831
it('loads schemas successfully', async () => {
3932
const node = new RedshiftDatabaseNode('testDB1', redshiftClient, connectionParams)
40-
listSchemasStub.returns({ promise: () => Promise.resolve({ Schemas: ['schema1'] }) })
33+
mockRedshiftData.on(ListSchemasCommand).resolves({ Schemas: ['schema1'] })
4134
const childNodes = await node.getChildren()
4235
verifyChildNodes(childNodes, false)
4336
})
4437

4538
it('loads schemas and shows load more node when there are more schemas', async () => {
4639
const node = new RedshiftDatabaseNode('testDB1', redshiftClient, connectionParams)
47-
listSchemasStub.returns({ promise: () => Promise.resolve({ Schemas: ['schema1'], NextToken: 'next' }) })
40+
mockRedshiftData.on(ListSchemasCommand).resolves({ Schemas: ['schema1'], NextToken: 'next' })
4841
const childNodes = await node.getChildren()
4942
verifyChildNodes(childNodes, true)
5043
})
5144

5245
it('shows error node when listSchema fails', async () => {
5346
const node = new RedshiftDatabaseNode('testDB1', redshiftClient, connectionParams)
54-
listSchemasStub.returns({ promise: () => Promise.reject('Failed') })
47+
mockRedshiftData.on(ListSchemasCommand).rejects('Failed')
5548
const childNodes = await node.getChildren()
5649
assert.strictEqual(childNodes.length, 1)
5750
assert.strictEqual(childNodes[0].contextValue, 'awsErrorNode')

0 commit comments

Comments
 (0)