Skip to content

Commit b073e04

Browse files
committed
refactor spring boot app, refactor lambda, update dependencies
1 parent 61b67f7 commit b073e04

File tree

31 files changed

+262
-281
lines changed

31 files changed

+262
-281
lines changed

pom.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>org.springframework.boot</groupId>
99
<artifactId>spring-boot-starter-parent</artifactId>
10-
<version>3.0.1</version>
10+
<version>3.2.4</version>
1111
</parent>
1212

1313
<groupId>dev.ancaghenade</groupId>
@@ -72,7 +72,7 @@
7272
<dependency>
7373
<groupId>org.json</groupId>
7474
<artifactId>json</artifactId>
75-
<version>20220924</version>
75+
<version>20231013</version>
7676
</dependency>
7777

7878
</dependencies>
@@ -82,7 +82,7 @@
8282
<dependency>
8383
<groupId>software.amazon.awssdk</groupId>
8484
<artifactId>bom</artifactId>
85-
<version>2.22.12</version>
85+
<version>2.26.28</version>
8686
<type>pom</type>
8787
<scope>import</scope>
8888
</dependency>
File renamed without changes.

shipment-list-demo-cloud-pod

-39.9 MB
Binary file not shown.
File renamed without changes.
File renamed without changes.

src/main/shipment-list-frontend/src/App.js shipment-list-frontend/src/App.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function Dropzone(
141141
(
142142
<div>
143143
<h3 style={{margin: 0}}>Size (Banana for scale):</h3>
144-
<h5 style={{margin: 0}}>click to add new image</h5>
144+
<h5 className="add-button">Click to add new image</h5>
145145
</div>)
146146

147147
}
@@ -154,7 +154,7 @@ function App() {
154154
<div className="App" style={{
155155
paddingBottom: "100px"
156156
}}>
157-
<h1>Shipments you can see and edit</h1>
157+
<h1>Shipment List</h1>
158158
<Shipments/>
159159
</div>
160160
);

src/main/shipment-list-frontend/src/index.css shipment-list-frontend/src/index.css

+13-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ body {
33
margin-top: 100px;
44

55
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
6-
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
7-
sans-serif;
6+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
7+
sans-serif;
88
-webkit-font-smoothing: antialiased;
99
-moz-osx-font-smoothing: grayscale;
1010
background-color: #eaeaea;
1111
}
1212

1313
code {
1414
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
15-
monospace;
15+
monospace;
1616
}
1717
body {
1818
font-family: 'Open Sans', sans-serif;
@@ -74,3 +74,13 @@ p {
7474
.btn:hover {
7575
background-color: #e72525;
7676
}
77+
78+
.add-button {
79+
background-color: #e5e023;
80+
padding: 5px 10px;
81+
display: inline-block;
82+
border: 2px solid black;
83+
border-radius: 50px;
84+
cursor: pointer;
85+
margin: 0; /* Ensure there's no margin */
86+
}

shipment-picture-lambda-validator/pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<dependency>
3939
<groupId>com.jayway.jsonpath</groupId>
4040
<artifactId>json-path</artifactId>
41-
<version>2.7.0</version>
41+
<version>2.9.0</version>
4242
<scope>compile</scope>
4343
</dependency>
4444
<dependency>
@@ -67,7 +67,7 @@
6767
<dependency>
6868
<groupId>software.amazon.awssdk</groupId>
6969
<artifactId>bom</artifactId>
70-
<version>2.22.12</version>
70+
<version>2.26.27</version>
7171
<type>pom</type>
7272
<scope>import</scope>
7373
</dependency>

shipment-picture-lambda-validator/src/main/java/dev/ancaghenade/shipmentpicturelambdavalidator/ServiceHandler.java

+98-86
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@
33
import com.amazonaws.services.lambda.runtime.Context;
44
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
55
import com.jayway.jsonpath.JsonPath;
6-
import java.awt.image.BufferedImage;
7-
import java.io.ByteArrayOutputStream;
8-
import java.io.File;
9-
import java.io.IOException;
10-
import java.io.InputStream;
11-
import java.io.OutputStream;
12-
import java.util.Collections;
13-
import java.util.List;
14-
import java.util.Objects;
15-
import javax.imageio.ImageIO;
166
import org.apache.http.entity.ContentType;
177
import software.amazon.awssdk.core.ResponseInputStream;
188
import software.amazon.awssdk.core.sync.RequestBody;
@@ -23,141 +13,163 @@
2313
import software.amazon.awssdk.services.sns.SnsClient;
2414
import software.amazon.awssdk.services.sns.model.PublishRequest;
2515

16+
import javax.imageio.ImageIO;
17+
import java.awt.image.BufferedImage;
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.io.OutputStream;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import java.util.Objects;
2626

2727
public class ServiceHandler implements RequestStreamHandler {
2828

2929
private static final String BUCKET_NAME = System.getenv("BUCKET");
30-
public ServiceHandler() {
31-
}
3230

3331
@Override
34-
public void handleRequest(InputStream inputStream, OutputStream outputStream,
35-
Context context) throws IOException {
36-
var isValid = true;
37-
38-
var s3Client = acquireS3Client();
39-
var snsClient = acquireSnsClient();
40-
var objectKey = getObjectKey(inputStream, context);
32+
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
33+
S3Client s3Client = acquireS3Client();
34+
SnsClient snsClient = acquireSnsClient();
35+
String objectKey = getObjectKey(inputStream, context);
4136

42-
if (Objects.isNull(objectKey)) {
37+
if (objectKey == null) {
4338
context.getLogger().log("Object key is null");
4439
return;
4540
}
4641

4742
context.getLogger().log("Object key: " + objectKey);
43+
processObject(s3Client, snsClient, objectKey, context);
44+
}
4845

49-
var getObjectRequest = GetObjectRequest.builder()
50-
.bucket(BUCKET_NAME)
51-
.key(objectKey)
52-
.build();
53-
54-
ResponseInputStream<GetObjectResponse> s3ObjectResponse;
46+
private void processObject(S3Client s3Client, SnsClient snsClient, String objectKey, Context context) {
5547
try {
56-
s3ObjectResponse = s3Client.getObject(
57-
getObjectRequest);
48+
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
49+
.bucket(BUCKET_NAME)
50+
.key(objectKey)
51+
.build();
52+
53+
ResponseInputStream<GetObjectResponse> s3ObjectResponse = s3Client.getObject(getObjectRequest);
54+
context.getLogger().log("Object fetched.");
55+
56+
if (isObjectAlreadyProcessed(s3ObjectResponse)) {
57+
context.getLogger().log("Object already present.");
58+
return;
59+
}
60+
61+
boolean isValid = validateObject(s3ObjectResponse, context);
62+
byte[] objectData = s3ObjectResponse.readAllBytes();
63+
String newObjectKey = objectKey;
64+
if (!isValid) {
65+
newObjectKey = handleInvalidObject(s3Client, objectKey, context);
66+
} else {
67+
handleValidObject(s3Client, objectKey, s3ObjectResponse, objectData, context);
68+
}
69+
70+
publishToSns(snsClient, newObjectKey, context);
71+
5872
} catch (Exception e) {
59-
e.printStackTrace();
6073
context.getLogger().log(e.getMessage());
61-
return;
74+
} finally {
75+
closeClients(s3Client, snsClient);
6276
}
63-
context.getLogger().log("Object fetched");
77+
}
6478

65-
// Check if the image was already processed
66-
if (s3ObjectResponse.response().metadata().entrySet().stream().anyMatch(
67-
entry -> entry.getKey().equals("skip-processing") && entry.getValue().equals("true"))) {
68-
context.getLogger().log("Object already present.");
69-
return;
70-
}
79+
private boolean isObjectAlreadyProcessed(ResponseInputStream<GetObjectResponse> s3ObjectResponse) {
80+
return s3ObjectResponse.response().metadata().entrySet().stream()
81+
.anyMatch(entry -> entry.getKey().equals("skip-processing") && entry.getValue().equals("true"));
82+
}
7183

72-
// Check the file extension to determine the image format
73-
if (!List.of(ContentType.IMAGE_JPEG.getMimeType(),
84+
private boolean validateObject(ResponseInputStream<GetObjectResponse> s3ObjectResponse, Context context) {
85+
String contentType = s3ObjectResponse.response().contentType();
86+
boolean isValid = List.of(
87+
ContentType.IMAGE_JPEG.getMimeType(),
7488
ContentType.IMAGE_PNG.getMimeType(),
75-
ContentType.IMAGE_BMP.getMimeType())
76-
.contains(s3ObjectResponse.response().contentType())) {
77-
isValid = false;
78-
context.getLogger().log("Object invalid due to wrong format.");
89+
ContentType.IMAGE_BMP.getMimeType()
90+
).contains(contentType);
7991

92+
if (!isValid) {
93+
context.getLogger().log("Object invalid due to wrong format.");
8094
}
8195

82-
// Get the object data as a byte array
83-
var objectData = s3Client.getObject(getObjectRequest).readAllBytes();
96+
return isValid;
97+
}
8498

85-
if (!isValid) {
86-
try {
87-
File imageFile = new File("placeholder.jpg");
88-
BufferedImage image = ImageIO.read(imageFile);
99+
private String handleInvalidObject(S3Client s3Client, String objectKey, Context context) {
100+
try {
101+
File imageFile = new File("placeholder.jpg");
102+
BufferedImage image = ImageIO.read(imageFile);
89103

90-
ByteArrayOutputStream baos = new ByteArrayOutputStream();
104+
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
91105
ImageIO.write(image, "jpg", baos);
92106
byte[] imageBytes = baos.toByteArray();
93107

94-
objectKey = TextParser.replaceSufix(objectKey, "placeholder.jpg");
108+
objectKey = TextParser.replaceSuffix(objectKey, "placeholder.jpg");
109+
context.getLogger().log("NEW IMAGE LINK: " + objectKey);
110+
111+
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
112+
.bucket(BUCKET_NAME)
113+
.key(objectKey)
114+
.metadata(Collections.singletonMap("skip-processing", "true"))
115+
.build();
116+
117+
s3Client.putObject(putObjectRequest, RequestBody.fromBytes(imageBytes));
118+
}
119+
} catch (IOException e) {
120+
context.getLogger().log("Error handling invalid object: " + e.getMessage());
121+
}
122+
123+
return objectKey;
124+
}
95125

96-
System.out.println("NEW IMAGE LINK: " + objectKey);
126+
private void handleValidObject(S3Client s3Client, String objectKey, ResponseInputStream<GetObjectResponse> s3ObjectResponse, byte[] objectData, Context context) throws IOException {
127+
String extension = s3ObjectResponse.response().contentType();
97128

98-
var putObjectRequest = PutObjectRequest.builder()
129+
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
99130
.bucket(BUCKET_NAME)
100131
.key(objectKey)
101132
.metadata(Collections.singletonMap("skip-processing", "true"))
102133
.build();
103134

104-
s3Client.putObject(putObjectRequest, RequestBody.fromBytes(imageBytes));
105-
106-
baos.close();
107-
} catch (IOException e) {
108-
e.printStackTrace();
109-
}
110-
111-
} else {
112-
var extension = s3ObjectResponse.response().contentType();
135+
s3Client.putObject(putObjectRequest, RequestBody.fromBytes(Watermark.watermarkImage(objectData, extension.substring(extension.lastIndexOf("/") + 1))));
136+
context.getLogger().log("Watermark has been added.");
137+
}
113138

114-
var putObjectRequest = PutObjectRequest.builder()
115-
.bucket(BUCKET_NAME)
116-
.key(objectKey)
117-
.metadata(Collections.singletonMap("skip-processing", "true"))
118-
.build();
139+
private void publishToSns(SnsClient snsClient, String objectKey, Context context) {
140+
PublishRequest request = PublishRequest.builder()
141+
.message(objectKey)
142+
.topicArn(SNSClientHelper.topicARN())
143+
.build();
119144

120-
s3Client.putObject(putObjectRequest, RequestBody.fromBytes(
121-
Watermark.watermarkImage(objectData,
122-
extension.substring(extension.lastIndexOf("/") + 1))));
123-
context.getLogger().log("Watermark has been added.");
124-
}
125-
var request = PublishRequest
126-
.builder()
127-
.message(objectKey)
128-
.topicArn(SNSClientHelper.topicARN())
129-
.build();
130145
snsClient.publish(request);
131146
context.getLogger().log("Published to topic: " + request.topicArn());
147+
}
132148

133-
// Close clients
149+
private void closeClients(S3Client s3Client, SnsClient snsClient) {
134150
s3Client.close();
135151
snsClient.close();
136-
137152
}
138153

139154
private String getObjectKey(InputStream inputStream, Context context) {
140155
try {
141156
List<String> keys = JsonPath.read(inputStream, "$.Records[*].s3.object.key");
142-
if (keys.iterator().hasNext()) {
143-
return keys.iterator().next();
144-
}
157+
return keys.isEmpty() ? null : keys.get(0);
145158
} catch (IOException ioe) {
146-
context.getLogger().log("caught IOException reading input stream: " + ioe.getMessage());
159+
context.getLogger().log("Caught IOException reading input stream: " + ioe.getMessage());
160+
return null;
147161
}
148-
return null;
149162
}
150163

151164
private S3Client acquireS3Client() {
152165
try {
153166
return S3ClientHelper.getS3Client();
154167
} catch (IOException e) {
155-
throw new RuntimeException(e);
168+
throw new RuntimeException("Failed to acquire S3 client", e);
156169
}
157170
}
158171

159172
private SnsClient acquireSnsClient() {
160173
return SNSClientHelper.getSnsClient();
161174
}
162175
}
163-
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,17 @@
11
package dev.ancaghenade.shipmentpicturelambdavalidator;
22

3-
import java.util.regex.Pattern;
4-
53
public class TextParser {
64

7-
public static String replaceSufix(String input, String replacement) {
8-
String[] parts = input.split("-");
9-
10-
// Find the last UUID
11-
String lastUUID = "";
12-
for (int i = parts.length - 1; i >= 0; i--) {
13-
if (isUUID(parts[i])) {
14-
lastUUID = parts[i];
15-
break;
16-
}
17-
}
5+
public static String replaceSuffix(String input, String replacement) {
6+
int lastSlashIndex = input.lastIndexOf('/');
7+
int lastDashIndex = input.lastIndexOf('-');
188

19-
// Extract the part before the last UUID
20-
String partBeforeLastUUID = input.substring(0, input.lastIndexOf(lastUUID) - 1);
9+
if (lastSlashIndex == -1 || lastDashIndex == -1 || lastDashIndex < lastSlashIndex) {
10+
return input;
11+
}
2112

22-
// Replace the part after the last UUID with a new string
23-
String result = partBeforeLastUUID + "-" + replacement;
24-
return result;
25-
}
13+
String basePath = input.substring(0, lastDashIndex + 1);
2614

27-
// Check if a string is a valid UUID
28-
public static boolean isUUID(String input) {
29-
String uuidPattern = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
30-
return Pattern.matches(uuidPattern, input);
31-
}
32-
}
15+
return basePath + replacement;
16+
}
17+
}

0 commit comments

Comments
 (0)