Skip to content
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

Spring AWS S3 #10

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ module for each Spring feature that I test.
## Table of contents
- [Events in Spring](spring-events/README.md);
- [SpEL permission evaluator](spring-el-permission-evaluator/README.md)
- [AWS S3](spring-aws-s3/README.md)
34 changes: 17 additions & 17 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.pedrodovale.experimentingwith.spring</groupId>
<artifactId>experimenting-with-spring-parent</artifactId>
<version>0.1-SNAPSHOT</version>
<modules>
<module>spring-events</module>
<module>spring-el-permission-evaluator</module>
</modules>
<packaging>pom</packaging>
<groupId>com.pedrodovale.experimentingwith.spring</groupId>
<artifactId>experimenting-with-spring-parent</artifactId>
<version>0.1-SNAPSHOT</version>
<modules>
<module>spring-events</module>
<module>spring-aws-s3</module>
</modules>
<packaging>pom</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

</project>
21 changes: 21 additions & 0 deletions spring-aws-s3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# AWS S3

Playing around with AWS S3 in a Springboot application based
on [this documentation](https://docs.awspring.io/spring-cloud-aws/docs/3.2.0/reference/html/index.html#starter-dependencies)

## How to test it

From the root folder, execute:

> ./mvnw clean package \
> java -jar spring-aws-s3/target/app.jar --spring.config.additional-location=spring-aws-s3/src/main/resources/aws_credentials.properties

This will start the Springboot application on port 8080.
To interact with the API:

> curl -v --include --form books=@spring-aws-s3/src/test/resources/books.json http://localhost:8080/books/import
> --data '{"title":"A Study in Scarlet"}'

> curl http://localhost:8080/books

The response body should contain the imported books, populated with an `id`.
81 changes: 81 additions & 0 deletions spring-aws-s3/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pedrodovale.experimentingwith.spring</groupId>
<artifactId>experimenting-with-spring-parent</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>

<artifactId>spring-aws-s3</artifactId>
<properties>
<spring-boot-dependencies.version>3.3.5</spring-boot-dependencies.version>
<spring-cloud-aws-dependencies.version>3.2.0</spring-cloud-aws-dependencies.version>
<spring-boot-maven-plugin.version></spring-boot-maven-plugin.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-dependencies</artifactId>
<version>${spring-cloud-aws-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-s3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>

<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>package-with-build-info</id>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.pedrodovale.experimentingwith.spring.awss3;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties(BooksProperties.class)
public class AwsS3Application {
public static void main(String[] args) {
SpringApplication.run(AwsS3Application.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.pedrodovale.experimentingwith.spring.awss3;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import software.amazon.awssdk.annotations.NotNull;

import static com.pedrodovale.experimentingwith.spring.awss3.BooksProperties.PREFIX;

@ConfigurationProperties(prefix = PREFIX)
@Getter
@Setter
public class BooksProperties {

public static final String PREFIX = "com.pedrodovale.experimentingwith.spring.books";

@NotNull private String bucket;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.pedrodovale.experimentingwith.spring.awss3.controller;

import com.pedrodovale.experimentingwith.spring.awss3.model.Book;
import com.pedrodovale.experimentingwith.spring.awss3.service.BooksService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;

@RestController
@RequiredArgsConstructor
public class BooksController {

private final BooksService booksService;

@PostMapping(
value = "/books/import",
consumes = MULTIPART_FORM_DATA_VALUE,
produces = APPLICATION_JSON_VALUE)
public List<Book> importBooks(@RequestPart(name = "books") MultipartFile multipartFile) {
return booksService.processAndBackup(multipartFile);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.pedrodovale.experimentingwith.spring.awss3.model;

import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

@Getter
@Setter
public class Book implements Serializable {
private String id;
private String title;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.pedrodovale.experimentingwith.spring.awss3.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pedrodovale.experimentingwith.spring.awss3.BooksProperties;
import com.pedrodovale.experimentingwith.spring.awss3.model.Book;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

@Service
public class BooksService {

public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final S3Client s3Client;
private final String bucket;

public BooksService(S3Client s3Client, BooksProperties booksProperties) {
this.s3Client = s3Client;
this.bucket = booksProperties.getBucket();
}

public List<Book> processAndBackup(MultipartFile multipartFile) {
try (InputStream inputStream = multipartFile.getInputStream()) {
byte[] bytes = inputStream.readAllBytes();
List<Book> books = OBJECT_MAPPER.readValue(bytes, new TypeReference<>() {});
books.forEach(
book -> {
String bookId = UUID.randomUUID().toString();
book.setId(bookId);
s3Client.putObject(
PutObjectRequest.builder().key(bookId).bucket(bucket).build(),
RequestBody.fromBytes(bytes));
});
return books;
} catch (IOException e) {
throw new RuntimeException("problem retrieving local file", e);
}
}
}
20 changes: 20 additions & 0 deletions spring-aws-s3/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
spring:
cloud:
aws:
region:
static: eu-central-1
s3:
path-style-access-enabled: false

com:
pedrodovale:
experimentingwith:
spring:
books:
bucket: books-123

logging:
level:
io:
awspring:
cloud: debug
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.pedrodovale.experimentingwith.spring.awss3.controller;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.core.io.Resource;
import org.springframework.mock.web.MockPart;
import org.springframework.test.web.servlet.MockMvc;
import software.amazon.awssdk.services.s3.S3Client;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class BooksControllerTest {

@Autowired private MockMvc mockMvc;

@MockBean private S3Client s3Client;

@Value("classpath:books.json")
private Resource sampleFile;

@Test
void importBooks() throws Exception {
mockMvc
.perform(
multipart("/books/import")
.part(
new MockPart(
"books", sampleFile.getFilename(), sampleFile.getContentAsByteArray())))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON))
.andExpect(jsonPath("$[0].id").isNotEmpty())
.andExpect(jsonPath("$[0].title").value("A Study In Scarlet"));
}
}
5 changes: 5 additions & 0 deletions spring-aws-s3/src/test/resources/books.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{
"title": "A Study In Scarlet"
}
]
Loading