diff --git a/pom.xml b/pom.xml
index a87740f..e39201a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,7 +80,25 @@
1.17.3
test
-
+
+
+ org.apache.httpcomponents
+ httpcore
+ 4.4.15
+
+
+
+ org.apache.httpcomponents
+ httpmime
+ 4.5.13
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+
+
+
shared-folder-server
diff --git a/src/main/java/com/project/sharedfolderserver/v1/file/FileDto.java b/src/main/java/com/project/sharedfolderserver/v1/file/FileDto.java
index 518389a..53a55cb 100644
--- a/src/main/java/com/project/sharedfolderserver/v1/file/FileDto.java
+++ b/src/main/java/com/project/sharedfolderserver/v1/file/FileDto.java
@@ -6,13 +6,14 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+import java.io.Serializable;
import java.time.Instant;
import java.util.UUID;
@Data
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
-public class FileDto {
+public class FileDto implements Serializable {
@JsonProperty("id")
private UUID id;
@JsonProperty("name")
diff --git a/src/main/java/com/project/sharedfolderserver/v1/file/FileHttpController.java b/src/main/java/com/project/sharedfolderserver/v1/file/FileHttpController.java
index 8c452c0..5a84f8e 100644
--- a/src/main/java/com/project/sharedfolderserver/v1/file/FileHttpController.java
+++ b/src/main/java/com/project/sharedfolderserver/v1/file/FileHttpController.java
@@ -6,10 +6,14 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
+import java.io.IOException;
import java.util.List;
import java.util.UUID;
@@ -28,20 +32,22 @@ public ResponseEntity> list() {
body(files);
}
- @PostMapping
- public ResponseEntity create(@Validate(JsonSchema.FILE_CREATE) FileDto fileToAdd) {
- log.info("in create, request body: " + fileToAdd);
- FileDto addedFile = fileService.create(fileToAdd);
+ @PostMapping(consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
+ public ResponseEntity create (@RequestParam("file") MultipartFile file) throws IOException {
+ FileDto uploadedFile = new FileDto();
+ uploadedFile.setName(file.getOriginalFilename());
+ uploadedFile.setContent(file.getBytes());
+ log.info("in create, request body: " + uploadedFile);
+ FileDto addedFile = fileService.create(uploadedFile);
return ResponseEntity.status(HttpStatus.CREATED)
.body(addedFile);
}
@GetMapping("{id}")
- public ResponseEntity download(@PathVariable UUID id) {
+ public StreamingResponseBody download(@PathVariable UUID id) {
FileDto file = fileService.findById(id)
.orElseThrow(() -> new FileNotFoundError(id));
- return ResponseEntity.status(HttpStatus.OK)
- .body(file);
+ return outputStream -> outputStream.write(file.getContent());
}
@DeleteMapping("{id}")
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 27bff83..d8986cf 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -4,6 +4,10 @@ migration:
baselineOnMigration: ${DB_MIGRATION_BASELINE_ENABLED:true}
spring:
+ servlet:
+ multipart:
+ max-file-size: 200MB
+ max-request-size: 200MB
flyway:
# should be disabled as we use our migration
enabled: false
diff --git a/src/test/java/com/project/sharedfolderserver/file/FileHttpControllerIT.java b/src/test/java/com/project/sharedfolderserver/file/FileHttpControllerIT.java
index 5e911fa..b949b6a 100644
--- a/src/test/java/com/project/sharedfolderserver/file/FileHttpControllerIT.java
+++ b/src/test/java/com/project/sharedfolderserver/file/FileHttpControllerIT.java
@@ -3,28 +3,42 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.IntNode;
+import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
import com.project.sharedfolderserver.BaseIT;
import com.project.sharedfolderserver.TestUtils;
import com.project.sharedfolderserver.v1.file.FileDto;
import com.project.sharedfolderserver.v1.utils.error.Error;
import com.project.sharedfolderserver.v1.utils.http.Response;
import com.project.sharedfolderserver.v1.utils.json.JSON;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
-import org.junit.jupiter.params.provider.NullAndEmptySource;
-import org.junit.jupiter.params.provider.ValueSource;
+import org.junit.jupiter.params.provider.*;
import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.*;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
@@ -76,25 +90,28 @@ void successGetFileList() throws IOException {
@Test
@Sql(scripts = {SQL_SCRIPTS_PATH + "cleanTables.sql", SQL_SCRIPTS_PATH + "downloadFile.sql"})
void successDownload() throws IOException {
+ //preparing
initializeCaseTest("file/success-download-file");
- FileDto expectedData = JSON.objectMapper.convertValue(expectedResult.get("data"), new TypeReference<>() {
- });
-
- ResponseEntity> response =
- restTemplate.exchange(
- getUrl(preRequest.get("path").asText())
- , HttpMethod.valueOf(preRequest.get("method").asText())
- , null
- , new ParameterizedTypeReference<>() {
- });
-
- assertNotNull(response, "expect to have a response");
- Response actualBody = response.getBody();
- assertNotNull(actualBody, "expect to have a body in the response");
- assertTrue(CollectionUtils.isEmpty(actualBody.getErrors()), "expect no errors");
- FileDto actualData = actualBody.getData();
- assertNotNull(actualData, "expect to have a file in the response");
- assertEquals(expectedData, actualData, "expect the get a file with content");
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpGet getRequest = new HttpGet(getUrl(preRequest.get("path").asText()));
+
+ // executing
+ try (CloseableHttpResponse response = client.execute(getRequest)) {
+
+ // validating
+ assertNotNull(response, "expect to have a response");
+ assertNotNull(response.getStatusLine(), "expect to have a status");
+ assertEquals(HttpStatus.OK.value(), response.getStatusLine().getStatusCode()," expect ok status code");
+ byte[] fileContent = response.getEntity().getContent().readAllBytes();
+ assertNotNull(fileContent, "expect to have a body in the response");
+ JsonNode expectedData = expectedResult.get("data");
+ assertEquals(Long.valueOf(expectedData.get("size").asText().split(" ")[0]), fileContent.length, "expect the same length");
+ assertEquals(expectedData.get("content").asText().getBytes().length, fileContent.length, "expect the same length");
+ for (int i=0; i() {
});
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPost postRequest = new HttpPost(getUrl(preRequest.get("path").asText()));
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+ builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+ builder.addBinaryBody("file", preRequest.get("body").get("content").asText().getBytes(), ContentType.DEFAULT_BINARY, expectedData.getName());
+ org.apache.http.HttpEntity entity = builder.build();
+ postRequest.setEntity(entity);
+
+ // executing
+ try (CloseableHttpResponse response = client.execute(postRequest)) {
+
+ // validating
+ assertNotNull(response, "expect to have a response");
+ assertNotNull(response.getStatusLine(), "expect to have a status");
+ assertEquals(HttpStatus.CREATED.value(), response.getStatusLine().getStatusCode()," expect created status code");
+ Response actualBody = JSON.objectMapper.readValue(response.getEntity().getContent(), new TypeReference<>() {
+ });
+ assertNotNull(actualBody, "expect to have a body in the response");
+ assertTrue(CollectionUtils.isEmpty(actualBody.getErrors()), "expect no errors");
+ FileDto actualData = actualBody.getData();
+ assertNotNull(actualData, "expect to have a file in the response");
+ TestUtils.assertEqualsExcludedFields(expectedData, actualData, "id", "dateAdded", "dateModified");
+ }
+ }
- ResponseEntity> response =
- restTemplate.exchange(getUrl(preRequest.get("path").asText())
- , HttpMethod.valueOf(preRequest.get("method").asText())
- , new HttpEntity<>(preRequest.get("body"))
- , new ParameterizedTypeReference<>() {
- });
-
- assertNotNull(response, "expect to have a response");
- Response actualBody = response.getBody();
- assertNotNull(actualBody, "expect to have a body in the response");
- assertTrue(CollectionUtils.isEmpty(actualBody.getErrors()), "expect no errors");
- FileDto actualData = actualBody.getData();
- assertNotNull(actualData, "expect to have fileDto in the response");
- TestUtils.assertEqualsExcludedFields(expectedData, actualData, "id", "dateAdded", "dateModified");
}
@DisplayName("Failed: upload file with existing name")
@Test
@Sql(scripts = {SQL_SCRIPTS_PATH + "cleanTables.sql", SQL_SCRIPTS_PATH + "uploadFileExistingName.sql"})
void failedUploadExistingName() throws IOException {
+ // preparing
initializeCaseTest("file/failed-upload-file-existing-name");
List expectedErrors = JSON.objectMapper.convertValue(expectedResult.get("errors"), new TypeReference<>() {
});
-
- ResponseEntity> response =
- restTemplate.exchange(getUrl(preRequest.get("path").asText())
- , HttpMethod.valueOf(preRequest.get("method").asText())
- , new HttpEntity<>(preRequest.get("body"))
- , new ParameterizedTypeReference<>() {
- });
-
- assertNotNull(response, "expect to have a response");
- Response actualBody = response.getBody();
- assertNotNull(actualBody, "expect to have a body in the response");
- assertFalse(CollectionUtils.isEmpty(actualBody.getErrors()), "expected to get errors");
- FileDto actualData = actualBody.getData();
- assertNull(actualData, "expect data to be null");
- assertEquals(expectedErrors, actualBody.getErrors(), "expected the same errors");
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPost postRequest = new HttpPost(getUrl(preRequest.get("path").asText()));
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+ builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+ builder.addBinaryBody("file", preRequest.get("body").get("content").asText().getBytes(), ContentType.DEFAULT_BINARY, preRequest.get("body").get("name").asText());
+ org.apache.http.HttpEntity entity = builder.build();
+ postRequest.setEntity(entity);
+
+ // executing
+ try (CloseableHttpResponse response = client.execute(postRequest)) {
+
+ // validating
+ assertNotNull(response, "expect to have a response");
+ assertNotNull(response.getStatusLine(), "expect to have a status");
+ assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusLine().getStatusCode()," expect bad request status code");
+ Response actualBody = JSON.objectMapper.readValue(response.getEntity().getContent(), new TypeReference<>() {
+ });
+ assertNotNull(actualBody, "expect to have a body in the response");
+ assertFalse(CollectionUtils.isEmpty(actualBody.getErrors()), "expected to get errors");
+ FileDto actualData = actualBody.getData();
+ assertNull(actualData, "expect data to be null");
+ assertEquals(expectedErrors, actualBody.getErrors(), "expected the same errors");
+ }
+ }
}
@DisplayName("Failed: upload file with illegal name")
@@ -189,69 +227,90 @@ void failedUploadExistingName() throws IOException {
@NullAndEmptySource
@ValueSource(strings = {"badname", "usik^*.ld", "=-0", " ggg.file.s"})
void failedUploadIllegalName(String badname) throws IOException {
+ // preparing
initializeCaseTest("file/failed-upload-file-illegal-name");
List expectedErrors = JSON.objectMapper.convertValue(expectedResult.get("errors"), new TypeReference<>() {
});
String errorMessage;
- if (badname == null || badname.isEmpty()) {
+ if (badname != null && badname.isEmpty()) {
errorMessage = "file could not be created. file name can not be empty";
} else {
errorMessage = String.format("file could not be created. Illegal file name %s, file name must be in the form of NAME.KIND, using letters, numbers. some special characters are illegal", badname);
}
expectedErrors.stream().findFirst().ifPresent(error -> error.setMessage(errorMessage));
((ObjectNode) (preRequest.get("body"))).put("name", badname);
-
- ResponseEntity> response =
- restTemplate.exchange(getUrl(preRequest.get("path").asText())
- , HttpMethod.valueOf(preRequest.get("method").asText())
- , new HttpEntity<>(preRequest.get("body"))
- , new ParameterizedTypeReference<>() {
- });
-
- assertNotNull(response, "expect to have a response");
- Response actualBody = response.getBody();
- assertNotNull(actualBody, "expect to have a body in the response");
- assertFalse(CollectionUtils.isEmpty(actualBody.getErrors()), "expected to get errors");
- FileDto actualData = actualBody.getData();
- assertNull(actualData, "expect data to be null");
- assertEquals(expectedErrors, actualBody.getErrors(), "expected the same errors");
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPost postRequest = new HttpPost(getUrl(preRequest.get("path").asText()));
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+ builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+ builder.addBinaryBody("file", preRequest.get("body").get("content").asText().getBytes(), ContentType.DEFAULT_BINARY, preRequest.get("body").get("name").asText());
+ org.apache.http.HttpEntity entity = builder.build();
+ postRequest.setEntity(entity);
+
+ // executing
+ try (CloseableHttpResponse response = client.execute(postRequest)) {
+
+ // validating
+ assertNotNull(response, "expect to have a response");
+ assertNotNull(response.getStatusLine(), "expect to have a status");
+ assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusLine().getStatusCode(), " expect bad request status code");
+ Response actualBody = JSON.objectMapper.readValue(response.getEntity().getContent(), new TypeReference<>() {
+ });
+ assertNotNull(actualBody, "expect to have a body in the response");
+ assertFalse(CollectionUtils.isEmpty(actualBody.getErrors()), "expected to have errors");
+ FileDto actualData = actualBody.getData();
+ assertNull(actualData, "expect data to be null");
+ assertEquals(expectedErrors, actualBody.getErrors(), "expected the same errors");
+
+ }
+ }
}
@DisplayName("Failed: upload file with illegal content")
@ParameterizedTest
@MethodSource("generateIllegalUploadRequestParameters")
- void failedUploadIllegalRequestParameters(String fieldName, JsonNode illegalFieldContent, String errorMessage) throws IOException {
+ void failedUploadIllegalRequestParameters( JsonNode illegalFieldContent, String errorMessage) throws IOException {
+ // preparing
initializeCaseTest("file/failed-upload-file-illegal-content");
List expectedErrors = JSON.objectMapper.convertValue(expectedResult.get("errors"), new TypeReference<>() {
});
Error expectedError = expectedErrors.stream().findFirst().orElseThrow();
- ((ObjectNode) (preRequest.get("body"))).set(fieldName, illegalFieldContent);
- expectedError.setMessage(expectedError.getMessage().replace(FIELD_NAME_REPLACE_SIGN, fieldName).replace(TYPE_NAME_REPLACE_SIGN, errorMessage));
-
- ResponseEntity> response =
- restTemplate.exchange(getUrl(preRequest.get("path").asText())
- , HttpMethod.valueOf(preRequest.get("method").asText())
- , new HttpEntity<>(preRequest.get("body"))
- , new ParameterizedTypeReference<>() {
- });
-
- assertNotNull(response, "expect to have a response");
- Response actualBody = response.getBody();
- assertNotNull(actualBody, "expect to have a body in the response");
- assertFalse(CollectionUtils.isEmpty(actualBody.getErrors()), "expected to get errors");
- FileDto actualData = actualBody.getData();
- assertNull(actualData, "expect data to be null");
- assertEquals(expectedErrors, actualBody.getErrors(), "expected the same errors");
+ ((ObjectNode) (preRequest.get("body"))).put("name", illegalFieldContent.toString());
+ expectedError.setMessage(expectedError.getMessage().replace(FIELD_NAME_REPLACE_SIGN, errorMessage));
+
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPost postRequest = new HttpPost(getUrl(preRequest.get("path").asText()));
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+ builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+ builder.addBinaryBody("file", preRequest.get("body").get("content").asText().getBytes(), ContentType.DEFAULT_BINARY, illegalFieldContent.toString());
+ org.apache.http.HttpEntity entity = builder.build();
+ postRequest.setEntity(entity);
+
+ // executing
+ try (CloseableHttpResponse response = client.execute(postRequest)) {
+
+ // validating
+ assertNotNull(response, "expect to have a response");
+ assertNotNull(response.getStatusLine(), "expect to have a status");
+ assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusLine().getStatusCode(), " expect bad request status code");
+ Response actualBody = JSON.objectMapper.readValue(response.getEntity().getContent(), new TypeReference<>() {
+ });
+ assertNotNull(actualBody, "expect to have a body in the response");
+ assertFalse(CollectionUtils.isEmpty(actualBody.getErrors()), "expected to have errors");
+ FileDto actualData = actualBody.getData();
+ assertNull(actualData, "expect data to be null");
+ assertEquals(expectedErrors, actualBody.getErrors(), "expected the same errors");
+ }
+ }
}
private Stream generateIllegalUploadRequestParameters() {
return Stream.of(
- Arguments.of("name", null, "null"),
- Arguments.of("content", null, "null"),
- Arguments.of("content", JSON.objectMapper.createArrayNode(), "array"),
- Arguments.of("name", JSON.objectMapper.createArrayNode(), "array"),
- Arguments.of("content", new IntNode(8), "integer"),
- Arguments.of("name", new IntNode(8), "integer")
+ Arguments.of(JSON.objectMapper.createArrayNode(), "[]"),
+ Arguments.of( new IntNode(8), "8"),
+ Arguments.of( new LongNode(8L), "8"),
+ Arguments.of( JSON.object(),"{}")
+
);
}
diff --git a/src/test/resources/cases/file/failed-upload-file-illegal-content.json b/src/test/resources/cases/file/failed-upload-file-illegal-content.json
index fa11dcd..e7e1877 100644
--- a/src/test/resources/cases/file/failed-upload-file-illegal-content.json
+++ b/src/test/resources/cases/file/failed-upload-file-illegal-content.json
@@ -11,8 +11,8 @@
"data": null,
"errors": [
{
- "name": "ValidationError",
- "message": "validation error [$.%fieldName%: %typeName% found, string expected]"
+ "name": "FileCannotBeCreatedError",
+ "message": "file could not be created. Illegal file name %fieldName%, file name must be in the form of NAME.KIND, using letters, numbers. some special characters are illegal"
}
]
}
diff --git a/src/test/resources/cases/file/success-download-file.json b/src/test/resources/cases/file/success-download-file.json
index 509005e..20cce83 100644
--- a/src/test/resources/cases/file/success-download-file.json
+++ b/src/test/resources/cases/file/success-download-file.json
@@ -6,12 +6,12 @@
"expectedResult": {
"data": {
"kind": "zip",
- "content": "QVJsRUFBPT0=",
+ "content": "ARlEAA==",
"id": "28cbd8e2-4b93-11ed-bdc3-0242ac120002",
"name": "testFile.zip",
"dateModified": 1436224954.000000000,
"dateAdded": 1436224954.000000000,
- "size": "4 bytes"
+ "size": "8 bytes"
}
}
}
\ No newline at end of file
diff --git a/src/test/resources/cases/file/success-upload-file.json b/src/test/resources/cases/file/success-upload-file.json
index ec64770..b66d785 100644
--- a/src/test/resources/cases/file/success-upload-file.json
+++ b/src/test/resources/cases/file/success-upload-file.json
@@ -14,7 +14,7 @@
"name": "testFile.zip",
"dateModified": "2022-10-17T08:29:13.096594Z",
"dateAdded": "2022-10-17T08:29:13.096553Z",
- "size": "4 bytes"
+ "size": "8 bytes"
}
}
}
\ No newline at end of file