Skip to content

Commit 791a501

Browse files
committed
Fix #1402 Migrating AWS S3 SDK to v2
1 parent 46dad04 commit 791a501

File tree

11 files changed

+230
-227
lines changed

11 files changed

+230
-227
lines changed

bolt-jakarta-servlet/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
<scope>test</scope>
6262
</dependency>
6363
<dependency>
64-
<groupId>com.amazonaws</groupId>
65-
<artifactId>aws-java-sdk-s3</artifactId>
64+
<groupId>software.amazon.awssdk</groupId>
65+
<artifactId>s3</artifactId>
6666
<version>${aws.s3.version}</version>
6767
<scope>test</scope>
6868
</dependency>

bolt-kotlin-examples/pom.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@
4141
</dependency>
4242

4343
<dependency>
44-
<groupId>com.amazonaws</groupId>
45-
<artifactId>aws-java-sdk-s3</artifactId>
44+
<groupId>software.amazon.awssdk</groupId>
45+
<artifactId>s3</artifactId>
4646
<version>${aws.s3.version}</version>
47+
<scope>provided</scope>
4748
</dependency>
4849

4950
<dependency>

bolt-servlet/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@
6666
<scope>test</scope>
6767
</dependency>
6868
<dependency>
69-
<groupId>com.amazonaws</groupId>
70-
<artifactId>aws-java-sdk-s3</artifactId>
69+
<groupId>software.amazon.awssdk</groupId>
70+
<artifactId>s3</artifactId>
7171
<version>${aws.s3.version}</version>
7272
<scope>test</scope>
7373
</dependency>

bolt/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
</dependency>
4242

4343
<dependency>
44-
<groupId>com.amazonaws</groupId>
45-
<artifactId>aws-java-sdk-s3</artifactId>
44+
<groupId>software.amazon.awssdk</groupId>
45+
<artifactId>s3</artifactId>
4646
<version>${aws.s3.version}</version>
4747
<scope>provided</scope>
4848
</dependency>

bolt/src/main/java/com/slack/api/bolt/service/builtin/AmazonS3InstallationService.java

Lines changed: 100 additions & 68 deletions
Large diffs are not rendered by default.

bolt/src/main/java/com/slack/api/bolt/service/builtin/AmazonS3OAuthStateService.java

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package com.slack.api.bolt.service.builtin;
22

3-
import com.amazonaws.auth.AWSCredentials;
4-
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
5-
import com.amazonaws.services.s3.AmazonS3;
6-
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
7-
import com.amazonaws.services.s3.model.AmazonS3Exception;
8-
import com.amazonaws.services.s3.model.PutObjectResult;
9-
import com.amazonaws.services.s3.model.S3Object;
10-
import com.amazonaws.util.IOUtils;
113
import com.slack.api.bolt.Initializer;
124
import com.slack.api.bolt.service.OAuthStateService;
13-
import com.slack.api.bolt.util.JsonOps;
145
import lombok.extern.slf4j.Slf4j;
6+
import software.amazon.awssdk.auth.credentials.AwsCredentials;
7+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
8+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
9+
import software.amazon.awssdk.core.ResponseBytes;
10+
import software.amazon.awssdk.core.sync.RequestBody;
11+
import software.amazon.awssdk.core.sync.ResponseTransformer;
12+
import software.amazon.awssdk.services.s3.S3Client;
13+
import software.amazon.awssdk.services.s3.model.*;
1514

16-
import java.io.IOException;
15+
import java.nio.charset.StandardCharsets;
1716

1817
/**
1918
* OAuthStateService implementation using Amazon S3.
@@ -24,6 +23,7 @@
2423
public class AmazonS3OAuthStateService implements OAuthStateService {
2524

2625
private final String bucketName;
26+
private AwsCredentialsProvider credentialsProvider;
2727

2828
public AmazonS3OAuthStateService(String bucketName) {
2929
this.bucketName = bucketName;
@@ -33,74 +33,91 @@ public AmazonS3OAuthStateService(String bucketName) {
3333
public Initializer initializer() {
3434
return (app) -> {
3535
// The first access to S3 tends to be slow on AWS Lambda.
36-
AWSCredentials credentials = getCredentials();
37-
if (credentials == null || credentials.getAWSAccessKeyId() == null) {
36+
this.credentialsProvider = DefaultCredentialsProvider.create();
37+
AwsCredentials credentials = createCredentials(credentialsProvider);
38+
if (credentials == null || credentials.accessKeyId() == null) {
3839
throw new IllegalStateException("AWS credentials not found");
3940
}
4041
if (log.isDebugEnabled()) {
41-
log.debug("AWS credentials loaded (access key id: {})", credentials.getAWSAccessKeyId());
42+
log.debug("AWS credentials loaded (access key id: {})", credentials.accessKeyId());
43+
}
44+
boolean bucketExists = false;
45+
Exception error = null;
46+
try (S3Client s3 = createS3Client()) {
47+
bucketExists = s3.headBucket(HeadBucketRequest.builder().bucket(bucketName).build()) != null;
48+
} catch (Exception e) { // NoSuchBucketException etc.
49+
error = e;
4250
}
43-
boolean bucketExists = createS3Client().doesBucketExistV2(bucketName);
4451
if (!bucketExists) {
45-
throw new IllegalStateException("Failed to access the Amazon S3 bucket (name: " + bucketName + ")");
52+
String message = "Failed to access the Amazon S3 bucket (name: " + bucketName + ", error: " + error + ")";
53+
throw new IllegalStateException(message);
4654
}
4755
};
4856
}
4957

5058
@Override
5159
public void addNewStateToDatastore(String state) throws Exception {
52-
AmazonS3 s3 = this.createS3Client();
53-
String value = "" + (System.currentTimeMillis() + getExpirationInSeconds() * 1000);
54-
PutObjectResult putObjectResult = s3.putObject(bucketName, getKey(state), value);
60+
PutObjectResponse putObjectResult;
61+
try (S3Client s3 = this.createS3Client()) {
62+
String value = "" + (System.currentTimeMillis() + getExpirationInSeconds() * 1000);
63+
putObjectResult = s3.putObject(
64+
PutObjectRequest.builder().bucket(bucketName).key(getKey(state)).build(),
65+
RequestBody.fromString(value)
66+
);
67+
}
5568
if (log.isDebugEnabled()) {
56-
log.debug("AWS S3 putObject result of state data - {}", JsonOps.toJsonString(putObjectResult));
69+
log.debug("AWS S3 putObject result of state data - {}", putObjectResult.toString());
5770
}
5871
}
5972

6073
@Override
6174
public boolean isAvailableInDatabase(String state) {
62-
AmazonS3 s3 = this.createS3Client();
63-
S3Object s3Object = getObject(s3, getKey(state));
75+
S3Client s3 = this.createS3Client();
76+
ResponseBytes<GetObjectResponse> s3Object = getObject(s3, getKey(state));
6477
if (s3Object == null) {
6578
return false;
6679
}
6780
String millisToExpire = null;
6881
try {
69-
millisToExpire = IOUtils.toString(s3Object.getObjectContent());
70-
return Long.valueOf(millisToExpire) > System.currentTimeMillis();
71-
} catch (IOException e) {
72-
log.error("Failed to load a state data for state: {}", state, e);
73-
return false;
82+
millisToExpire = s3Object.asString(StandardCharsets.UTF_8);
83+
return Long.parseLong(millisToExpire) > System.currentTimeMillis();
7484
} catch (NumberFormatException ne) {
7585
log.error("Invalid state value detected - state: {}, millisToExpire: {}", state, millisToExpire);
7686
return false;
87+
} catch (Exception e) {
88+
log.error("Failed to load a state data for state: {}", state, e);
89+
return false;
7790
}
7891
}
7992

8093
@Override
8194
public void deleteStateFromDatastore(String state) throws Exception {
82-
AmazonS3 s3 = this.createS3Client();
83-
s3.deleteObject(bucketName, getKey(state));
95+
try (S3Client s3 = this.createS3Client()) {
96+
s3.deleteObject(DeleteObjectRequest.builder().bucket(bucketName).key(getKey(state)).build());
97+
}
8498
}
8599

86-
protected AWSCredentials getCredentials() {
87-
return DefaultAWSCredentialsProviderChain.getInstance().getCredentials();
100+
protected AwsCredentials createCredentials(AwsCredentialsProvider provider) {
101+
return provider.resolveCredentials();
88102
}
89103

90-
protected AmazonS3 createS3Client() {
91-
return AmazonS3ClientBuilder.defaultClient();
104+
protected S3Client createS3Client() {
105+
return S3Client.builder().credentialsProvider(this.credentialsProvider).build();
92106
}
93107

94108
private String getKey(String state) {
95109
return "state/" + state;
96110
}
97111

98-
private S3Object getObject(AmazonS3 s3, String fullKey) {
112+
private ResponseBytes<GetObjectResponse> getObject(S3Client s3, String fullKey) {
99113
try {
100-
return s3.getObject(bucketName, fullKey);
101-
} catch (AmazonS3Exception e) {
114+
return s3.getObject(
115+
GetObjectRequest.builder().bucket(bucketName).key(fullKey).build(),
116+
ResponseTransformer.toBytes()
117+
);
118+
} catch (Exception e) {
102119
if (log.isDebugEnabled()) {
103-
log.debug("Amazon S3 object metadata not found (key: {}, AmazonS3Exception: {})", fullKey, e.toString());
120+
log.debug("Amazon S3 object metadata not found (key: {}, Exception: {})", fullKey, e.toString());
104121
}
105122
return null;
106123
}

bolt/src/test/java/test_locally/service/AmazonS3InstallationServiceTest.java

Lines changed: 36 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,44 @@
11
package test_locally.service;
22

3-
import com.amazonaws.SdkClientException;
4-
import com.amazonaws.auth.AWSCredentials;
5-
import com.amazonaws.services.s3.AmazonS3;
63
import com.slack.api.bolt.model.builtin.DefaultBot;
74
import com.slack.api.bolt.model.builtin.DefaultInstaller;
85
import com.slack.api.bolt.service.builtin.AmazonS3InstallationService;
96
import org.junit.Test;
7+
import software.amazon.awssdk.auth.credentials.AwsCredentials;
8+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
9+
import software.amazon.awssdk.core.ResponseBytes;
10+
import software.amazon.awssdk.core.sync.RequestBody;
11+
import software.amazon.awssdk.core.sync.ResponseTransformer;
12+
import software.amazon.awssdk.services.s3.S3Client;
13+
import software.amazon.awssdk.services.s3.model.*;
14+
15+
import java.nio.charset.StandardCharsets;
1016

1117
import static org.junit.Assert.assertNotNull;
1218
import static org.mockito.Mockito.*;
1319

1420
public class AmazonS3InstallationServiceTest {
1521

16-
@Test(expected = IllegalStateException.class)
17-
public void initializer_no_credentials() {
18-
AmazonS3 s3 = mock(AmazonS3.class);
19-
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
20-
@Override
21-
protected AWSCredentials getCredentials() {
22-
return null;
23-
}
24-
25-
@Override
26-
protected AmazonS3 createS3Client() {
27-
return s3;
28-
}
29-
};
30-
service.initializer().accept(null);
22+
static S3Client setupMocks(S3Client s3) {
23+
when(s3.headBucket((HeadBucketRequest) notNull())).thenReturn(HeadBucketResponse.builder().build());
24+
when(s3.headObject((HeadObjectRequest) notNull())).thenReturn(HeadObjectResponse.builder().build());
25+
when(s3.getObject((GetObjectRequest) notNull(), (ResponseTransformer<GetObjectResponse, ResponseBytes<GetObjectResponse>>) notNull()))
26+
.thenReturn(ResponseBytes.fromByteArray(GetObjectResponse.builder().build(), "{}".getBytes(StandardCharsets.UTF_8)));
27+
when(s3.putObject((PutObjectRequest) notNull(), (RequestBody) notNull())).thenReturn(PutObjectResponse.builder().build());
28+
return s3;
3129
}
3230

3331
@Test(expected = IllegalStateException.class)
34-
public void initializer_no_bucket() {
35-
AWSCredentials credentials = mock(AWSCredentials.class);
36-
AmazonS3 s3 = mock(AmazonS3.class);
32+
public void initializer_no_credentials() {
33+
S3Client s3 = setupMocks(mock(S3Client.class));
3734
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
3835
@Override
39-
protected AWSCredentials getCredentials() {
40-
return credentials;
36+
protected AwsCredentials createCredentials(AwsCredentialsProvider provider) {
37+
return null;
4138
}
4239

4340
@Override
44-
protected AmazonS3 createS3Client() {
41+
protected S3Client createS3Client() {
4542
return s3;
4643
}
4744
};
@@ -50,19 +47,13 @@ protected AmazonS3 createS3Client() {
5047

5148
@Test
5249
public void initializer() {
53-
AWSCredentials credentials = mock(AWSCredentials.class);
54-
when(credentials.getAWSAccessKeyId()).thenReturn("valid key");
55-
AmazonS3 s3 = mock(AmazonS3.class);
56-
when(s3.doesBucketExistV2(anyString())).thenReturn(true);
50+
AwsCredentials credentials = mock(AwsCredentials.class);
51+
when(credentials.accessKeyId()).thenReturn("valid key");
52+
S3Client s3 = setupMocks(mock(S3Client.class));
5753

5854
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
5955
@Override
60-
protected AWSCredentials getCredentials() {
61-
return credentials;
62-
}
63-
64-
@Override
65-
protected AmazonS3 createS3Client() {
56+
protected S3Client createS3Client() {
6657
return s3;
6758
}
6859
};
@@ -71,19 +62,13 @@ protected AmazonS3 createS3Client() {
7162

7263
@Test
7364
public void operations() throws Exception {
74-
AWSCredentials credentials = mock(AWSCredentials.class);
75-
when(credentials.getAWSAccessKeyId()).thenReturn("valid key");
76-
AmazonS3 s3 = mock(AmazonS3.class);
77-
when(s3.doesBucketExistV2(anyString())).thenReturn(true);
65+
AwsCredentials credentials = mock(AwsCredentials.class);
66+
when(credentials.accessKeyId()).thenReturn("valid key");
67+
S3Client s3 = setupMocks(mock(S3Client.class));
7868

7969
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
8070
@Override
81-
protected AWSCredentials getCredentials() {
82-
return credentials;
83-
}
84-
85-
@Override
86-
protected AmazonS3 createS3Client() {
71+
protected S3Client createS3Client() {
8772
return s3;
8873
}
8974
};
@@ -98,19 +83,13 @@ protected AmazonS3 createS3Client() {
9883

9984
@Test
10085
public void operations_historical_data_enabled() throws Exception {
101-
AWSCredentials credentials = mock(AWSCredentials.class);
102-
when(credentials.getAWSAccessKeyId()).thenReturn("valid key");
103-
AmazonS3 s3 = mock(AmazonS3.class);
104-
when(s3.doesBucketExistV2(anyString())).thenReturn(true);
86+
AwsCredentials credentials = mock(AwsCredentials.class);
87+
when(credentials.accessKeyId()).thenReturn("valid key");
88+
S3Client s3 = setupMocks(mock(S3Client.class));
10589

10690
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
10791
@Override
108-
protected AWSCredentials getCredentials() {
109-
return credentials;
110-
}
111-
112-
@Override
113-
protected AmazonS3 createS3Client() {
92+
protected S3Client createS3Client() {
11493
return s3;
11594
}
11695
};
@@ -129,32 +108,18 @@ public MyService(String bucketName) {
129108
super(bucketName);
130109
}
131110

132-
public AWSCredentials credentials() {
133-
return getCredentials();
134-
}
135-
136-
public AmazonS3 s3() {
111+
public S3Client s3() {
137112
return createS3Client();
138113
}
139114
}
140115

141-
@Test
142-
public void credentials() {
143-
MyService service = new MyService("test-bucket");
144-
try {
145-
AWSCredentials credentials = service.credentials();
146-
assertNotNull(credentials);
147-
} catch (SdkClientException ignored) {
148-
}
149-
}
150-
151116
@Test
152117
public void s3() {
153118
MyService service = new MyService("test-bucket");
154119
try {
155-
AmazonS3 s3 = service.s3();
120+
S3Client s3 = service.s3();
156121
assertNotNull(s3);
157-
} catch (SdkClientException ignored) {
122+
} catch (Exception ignored) {
158123
}
159124
}
160125

0 commit comments

Comments
 (0)