Skip to content

Commit

Permalink
-rename titles
Browse files Browse the repository at this point in the history
  • Loading branch information
tinyg210 committed Jun 12, 2023
1 parent b8e03c9 commit 3878044
Show file tree
Hide file tree
Showing 20 changed files with 165 additions and 66 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
| __Works on__ | LocalStack v2 |


### UPDATE

The Terraform configuration file now randomly generates names for the bucket, in order to avoid conflicts
at a global scale on AWS. This name shall be written out to a properties file, which the app will pick up
and use for the S3 client. Furthermore, the name is also passed as an environment variable to the Lambda function by Terraform,
so there's no need to worry about managing it.


## Introduction

This application was created for demonstration purposes to highlight the ease of switching from
Expand Down Expand Up @@ -137,7 +145,7 @@ $ export AWS_SECRET_ACCESS_KEY=[your_aws_secret_access_key_id]

Make sure you have Terraform [installed](https://developer.hashicorp.com/terraform/downloads)

Under setup/terraform run:
Under `terraform` run:

```
$ terraform init
Expand Down Expand Up @@ -210,12 +218,12 @@ to get rid of any files that keep track of the resources' state. Then:

```
$ tflocal init
$ tflocal plan -var 'env=dev'
$ tflocal plan
$ tflocal apply
```

What we're doing here is just passing an environmental variable to let the Lambda
know this is the `dev` environment.
We run the exact same commands for the exact same file. We no longer need to pass any environment
variables, since the bucket name is generated and passed by Terraform.

### Starting the backend

Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

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

11 changes: 0 additions & 11 deletions setup/terraform/vars.tf

This file was deleted.

6 changes: 6 additions & 0 deletions shipment-picture-lambda-validator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@
<artifactId>thumbnailator</artifactId>
<version>0.4.19</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>

<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.ancaghenade.shipmentpicturelambdavalidator;

import lombok.Getter;
import software.amazon.awssdk.regions.Region;

@Getter
public enum Location {


REGION(Region.US_EAST_1);

private final Region region;
Location(Region region) {
this.region = region;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@

import java.io.IOException;
import java.net.URI;
import software.amazon.awssdk.regions.Region;
import java.util.Objects;
import software.amazon.awssdk.services.s3.S3Client;

public class S3ClientHelper {

private static final String ENVIRONMENT = System.getenv("ENVIRONMENT");
private static PropertiesProvider properties = new PropertiesProvider();
private static final String LOCALSTACK_HOSTNAME = System.getenv("LOCALSTACK_HOSTNAME");

public static S3Client getS3Client() throws IOException {

var clientBuilder = S3Client.builder();
if (properties.getProperty("environment.dev").equals(ENVIRONMENT)) {

if (Objects.nonNull(LOCALSTACK_HOSTNAME)) {
return clientBuilder
.region(Region.of(properties.getProperty("aws.region")))
.endpointOverride(URI.create(properties.getProperty("s3.endpoint")))
.region(Location.REGION.getRegion())
.endpointOverride(URI.create(String.format("http://%s:4566", LOCALSTACK_HOSTNAME)))
.forcePathStyle(true)
.build();
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
package dev.ancaghenade.shipmentpicturelambdavalidator;

import java.io.IOException;
import java.net.URI;
import software.amazon.awssdk.regions.Region;
import java.util.Objects;
import software.amazon.awssdk.services.sns.SnsClient;

public class SNSClientHelper {

private static final String ENVIRONMENT = System.getenv("ENVIRONMENT");
private static PropertiesProvider properties = new PropertiesProvider();

private static final String LOCALSTACK_HOSTNAME = System.getenv("LOCALSTACK_HOSTNAME");
private static String snsTopicArn;

public static SnsClient getSnsClient() throws IOException {
public static SnsClient getSnsClient() {

var clientBuilder = SnsClient.builder();

if (properties.getProperty("environment.dev").equals(ENVIRONMENT)) {
snsTopicArn = properties.getProperty("sns.arn.dev");
if (Objects.nonNull(LOCALSTACK_HOSTNAME)) {
snsTopicArn = String.format("arn:aws:sns:%s:000000000000:update_shipment_picture_topic",
Location.REGION.getRegion());

return clientBuilder
.region(Region.of(properties.getProperty("aws.region")))
.endpointOverride(URI.create(properties.getProperty("sns.endpoint")))
.region(Location.REGION.getRegion())
.endpointOverride(URI.create(String.format("http://%s:4566", LOCALSTACK_HOSTNAME)))
.build();
} else {
snsTopicArn = properties.getProperty("sns.arn.prod");
snsTopicArn = String.format("arn:aws:sns:%s:%s:update_shipment_picture_topic",
Location.REGION.getRegion(), "932043840972");
return clientBuilder.build();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,19 @@
import java.util.Objects;
import javax.imageio.ImageIO;
import org.apache.http.entity.ContentType;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.PublishRequest;


public class ServiceHandler implements RequestStreamHandler {

private static final String BUCKET_NAME = "shipment-picture-bucket";

private static final String BUCKET_NAME = System.getenv("BUCKET");
public ServiceHandler() {
}

Expand All @@ -50,9 +51,15 @@ public void handleRequest(InputStream inputStream, OutputStream outputStream,
.key(objectKey)
.build();

var s3ObjectResponse = s3Client.getObject(
getObjectRequest);

ResponseInputStream<GetObjectResponse> s3ObjectResponse;
try {
s3ObjectResponse = s3Client.getObject(
getObjectRequest);
} catch (Exception e) {
e.printStackTrace();
context.getLogger().log(e.getMessage());
return;
}
context.getLogger().log("Object fetched");

// Check if the image was already processed
Expand Down Expand Up @@ -150,11 +157,7 @@ private S3Client acquireS3Client() {
}

private SnsClient acquireSnsClient() {
try {
return SNSClientHelper.getSnsClient();
} catch (IOException e) {
throw new RuntimeException(e);
}
return SNSClientHelper.getSnsClient();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

awslocal lambda update-function-code --function-name shipment-picture-lambda-validator \
--zip-file fileb://target/shipment-picture-lambda-validator.jar \
--region eu-central-1
--region us-east-1

aws lambda update-function-code --function-name shipment-picture-lambda-validator \
--zip-file fileb://target/shipment-picture-lambda-validator.jar \
--region eu-central-1
--region us-east-1
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
package dev.ancaghenade.shipmentlistdemo.buckets;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Getter
public enum BucketName {
@Component
@Configuration
@PropertySource(value = "classpath:buckets.properties")
public class BucketName {

SHIPMENT_PICTURE("shipment-picture-bucket");
@Value("${shipment-picture-bucket}")
private String shipmentPictureBucket;
@Value("${shipment-picture-bucket-validator}")
private String shipmentPictureValidatorBucket;

private final String bucketName;
public String getShipmentPictureBucket() {
return shipmentPictureBucket;
}

BucketName(String bucketName) {
this.bucketName = bucketName;
public String getShipmentPictureValidatorBucket() {
return shipmentPictureValidatorBucket;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public Optional<Shipment> getShipment(String shipmentId) {

public String delete(String shipmentId) {
shipmentTable.deleteItem(Key.builder().partitionValue(shipmentId).build());

return "Shipment has been deleted";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,43 @@
import dev.ancaghenade.shipmentlistdemo.buckets.BucketName;
import dev.ancaghenade.shipmentlistdemo.util.FileUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Error;
import software.amazon.awssdk.services.s3.model.S3Object;

@Service
public class S3StorageService {

private final S3Client s3;
private static final Logger LOGGER = LoggerFactory.getLogger(S3StorageService.class);

private final BucketName bucketName;
@Autowired
public S3StorageService(S3Client s3) {
public S3StorageService(S3Client s3, BucketName bucketName) {
this.s3 = s3;
this.bucketName = bucketName;
}

public void save(String path, String fileName,
MultipartFile multipartFile)
throws IOException {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(BucketName.SHIPMENT_PICTURE.getBucketName())
.bucket(bucketName.getShipmentPictureBucket())
.key(path + "/" + fileName)
.contentType(multipartFile.getContentType())
.contentLength(multipartFile.getSize())
Expand All @@ -42,7 +52,7 @@ public void save(String path, String fileName,

public byte[] download(String key) throws IOException {
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(BucketName.SHIPMENT_PICTURE.getBucketName())
.bucket(bucketName.getShipmentPictureBucket())
.key(key)
.build();
byte[] object = new byte[0];
Expand All @@ -54,4 +64,34 @@ public byte[] download(String key) throws IOException {
return object;
}

public void delete(String folderPrefix) {
List<ObjectIdentifier> keysToDelete = new ArrayList<>();
s3.listObjectsV2Paginator(
builder -> builder.bucket(bucketName.getShipmentPictureBucket())
.prefix(folderPrefix + "/"))
.contents().stream()
.map(S3Object::key)
.forEach(key -> keysToDelete.add(ObjectIdentifier.builder().key(key).build()));

DeleteObjectsRequest deleteRequest = DeleteObjectsRequest.builder()
.bucket(bucketName.getShipmentPictureBucket())
.delete(builder -> builder.objects(keysToDelete).build())
.build();

try {
DeleteObjectsResponse response = s3.deleteObjects(deleteRequest);
List<S3Error> errors = response.errors();
if (!errors.isEmpty()) {
LOGGER.error("Errors occurred while deleting objects:");
errors.forEach(error -> System.out.println("Object: " + error.key() +
", Error Code: " + error.code() +
", Error Message: " + error.message()));
} else {
LOGGER.info("Objects deleted successfully.");
}
} catch (SdkException e) {
LOGGER.error("Error occurred during object deletion: " + e.getMessage());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public List<Shipment> getAllShipments() {
}

public String deleteShipment(String shipmentId) {
s3StorageService.delete(shipmentId);
return dynamoDBService.delete(shipmentId);
}

Expand All @@ -59,12 +60,12 @@ public void uploadShipmentImage(String shipmentId, MultipartFile file) {
} catch (IOException e) {
throw new IllegalStateException(e);
}
shipment.setImageLink(fileName);
shipment.setImageLink(format("%s/%s", path, fileName));
dynamoDBService.upsert(shipment);
}


public byte[] downloadShipmentImage(String shipmentId) {
public byte[] downloadShipmentImage(String shipmentId) throws IllegalStateException {
Shipment shipment = dynamoDBService.getShipment(shipmentId).stream()
.findFirst()
.orElseThrow(
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ aws:
endpoint: http://localhost.localstack.cloud:4566/
sqs:
endpoint: http://localhost:4566/000000000000
region: eu-central-1
region: us-east-1
2 changes: 1 addition & 1 deletion src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ aws:
endpoint: https://s3.eu-central-1.amazonaws.com
sqs:
endpoint: https://sqs.eu-central-1.amazonaws.com
region: eu-central-1
region: us-east-1
2 changes: 2 additions & 0 deletions src/main/resources/buckets.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
shipment-picture-bucket=shipment-picture-bucket-concise-malamute
shipment-picture-bucket-validator=shipment-picture-lambda-validator-bucket-concise-malamute
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 3878044

Please sign in to comment.