Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
import com.callv2.drive.domain.file.File;
import com.callv2.drive.domain.file.FileGateway;
import com.callv2.drive.domain.file.FileID;
import com.callv2.drive.domain.storage.StorageService;

public class DefaultGetFileContentUseCase extends GetFileContentUseCase {

private final FileGateway fileGateway;
private final StorageService storageService;

public DefaultGetFileContentUseCase(final FileGateway fileGateway) {
public DefaultGetFileContentUseCase(
final FileGateway fileGateway,
final StorageService storageService) {
this.fileGateway = Objects.requireNonNull(fileGateway);
this.storageService = Objects.requireNonNull(storageService);
}

@Override
Expand All @@ -26,8 +31,9 @@ public GetFileContentOutput execute(GetFileContentInput input) {

return GetFileContentOutput.with(
file.getName().value(),
file.getContent().location(),
file.getContent().size());
file.getContent().size(),
storageService.retrieve(file.getContent().storageKey()));

}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.callv2.drive.application.file.content.get;

public record GetFileContentOutput(String name, String location, long size) {
import java.io.InputStream;

public static GetFileContentOutput with(final String name, final String location, final long size) {
return new GetFileContentOutput(name, location, size);
public record GetFileContentOutput(String name, long size, InputStream inputStream) {

public static GetFileContentOutput with(final String name, final long size, InputStream inputStream) {
return new GetFileContentOutput(name, size, inputStream);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.callv2.drive.domain.exception.InternalErrorException;
import com.callv2.drive.domain.exception.NotFoundException;
import com.callv2.drive.domain.exception.QuotaExceededException;
import com.callv2.drive.domain.exception.StorageKeyAlreadyExistsException;
import com.callv2.drive.domain.exception.ValidationException;
import com.callv2.drive.domain.file.Content;
import com.callv2.drive.domain.file.File;
Expand Down Expand Up @@ -77,12 +78,11 @@ public CreateFileOutput execute(final CreateFileInput input) {
throw ValidationException.with("Could not create Aggregate File",
ValidationError.with("File with same name already exists on this folder"));

final String randomContentName = UUID.randomUUID().toString();
final String contentLocation = storeContentFile(randomContentName, input.content());
final String storageKey = storeContentFile(input.content());
final String contentType = input.contentType();
final Long contentSize = input.size();

final Content content = Content.of(contentLocation, contentType, contentSize);
final Content content = Content.of(storageKey, contentType, contentSize);

final File file = notification.validate(() -> File.create(ownerId, folderId, fileName, content));
if (notification.hasError())
Expand All @@ -97,17 +97,31 @@ private void storeFile(final File file) {
try {
fileGateway.create(file);
} catch (Exception e) {
deleteContentFile(file.getContent().location());
deleteContentFile(file.getContent().storageKey());
throw InternalErrorException.with("Could not store File", e);
}
}

private String storeContentFile(final String contentName, final InputStream inputStream) {
private String storeContentFile(final InputStream inputStream) {

try {
return storageService.store(contentName, inputStream);

do {

try {
final String key = UUID.randomUUID().toString();
storageService.store(key, inputStream);
return key;
} catch (StorageKeyAlreadyExistsException e) {
continue;
}

} while (true);

} catch (Exception e) {
throw InternalErrorException.with("Could not store BinaryContent", e);
}

}

private void deleteContentFile(final String contentLocation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void execute(final DeleteFileInput input) {
.orElseThrow(() -> NotFoundException.with(File.class, input.fileId().toString()));

fileGateway.deleteById(file.getId()); // TODO maybe needs transactional
deleteContentFile(file.getContent().location());
deleteContentFile(file.getContent().storageKey());
}

private void deleteContentFile(final String contentLocation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.util.Optional;

import org.junit.jupiter.api.Test;
Expand All @@ -24,6 +25,7 @@
import com.callv2.drive.domain.file.FileName;
import com.callv2.drive.domain.folder.FolderID;
import com.callv2.drive.domain.member.MemberID;
import com.callv2.drive.domain.storage.StorageService;

@ExtendWith(MockitoExtension.class)
public class DefaultGetFileContentUseCaseTest {
Expand All @@ -34,26 +36,33 @@ public class DefaultGetFileContentUseCaseTest {
@Mock
FileGateway fileGateway;

@Mock
StorageService storageService;

@Test
void givenAValidParam_whenCallsExecute_shouldReturnContent() {

final var ownerId = MemberID.of("owner");
final var expectedFolder = FolderID.unique();
final var expectedFileName = FileName.of("file.txt");
final var expectedContent = Content.of("location", "text", 10);
final var expectedContent = Content.of("key", "text", 10);
final var expectedFile = File.create(ownerId, expectedFolder, expectedFileName, expectedContent);
final var expectedFileId = expectedFile.getId();
final var expectedInputStream = new ByteArrayInputStream(new byte[] {});

when(fileGateway.findById(any()))
.thenReturn(Optional.of(expectedFile));

when(storageService.retrieve(expectedContent.storageKey()))
.thenReturn(expectedInputStream);

final var input = GetFileContentInput.with(expectedFileId.getValue());

final var actualOutput = useCase.execute(input);

assertEquals(expectedFileName.value(), actualOutput.name());
assertEquals(expectedContent.location(), actualOutput.location());
assertEquals(expectedContent.size(), actualOutput.size());
assertEquals(expectedInputStream, actualOutput.inputStream());

verify(fileGateway, times(1)).findById(any());
verify(fileGateway, times(1)).findById(eq(expectedFileId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
import com.callv2.drive.domain.storage.StorageService;

@ExtendWith(MockitoExtension.class)
public class DefaultCreateFileUseCaseTest {
class DefaultCreateFileUseCaseTest {

@InjectMocks
DefaultCreateFileUseCase useCase;
Expand Down Expand Up @@ -99,8 +99,8 @@ void givenAValidParams_whenCallsExecute_thenShouldCreateFile() {
when(folderGateway.findById(any()))
.thenReturn(Optional.of(folder));

when(storageService.store(any(), any()))
.then(returnsFirstArg());
doNothing()
.when(storageService).store(any(), any());

when(fileGateway.create(any()))
.thenAnswer(returnsFirstArg());
Expand Down Expand Up @@ -132,7 +132,7 @@ void givenAValidParams_whenCallsExecute_thenShouldCreateFile() {
assertEquals(expectedContentType, file.getContent().type());
assertNotNull(file.getCreatedAt());
assertNotNull(file.getUpdatedAt());
assertNotNull(file.getContent().location());
assertNotNull(file.getContent().storageKey());
assertEquals(file.getCreatedAt(), file.getUpdatedAt());

return true;
Expand Down Expand Up @@ -356,8 +356,8 @@ void givenAValidParams_whenCallsExecuteAndFileGatewayCreateThrowsRandomException
when(folderGateway.findById(any()))
.thenReturn(Optional.of(folder));

when(storageService.store(any(), any()))
.then(returnsFirstArg());
doNothing()
.when(storageService).store(any(), any());

when(fileGateway.create(any()))
.thenThrow(new IllegalStateException("FileGateway Exception"));
Expand Down Expand Up @@ -394,7 +394,7 @@ void givenAValidParams_whenCallsExecuteAndFileGatewayCreateThrowsRandomException
assertEquals(expectedContentType, file.getContent().type());
assertNotNull(file.getCreatedAt());
assertNotNull(file.getUpdatedAt());
assertNotNull(file.getContent().location());
assertNotNull(file.getContent().storageKey());
assertEquals(file.getCreatedAt(), file.getUpdatedAt());

return true;
Expand Down Expand Up @@ -441,8 +441,8 @@ void givenAValidParams_whenCallsExecuteAndFileGatewayCreateAndContentGatewayDele
when(folderGateway.findById(any()))
.thenReturn(Optional.of(folder));

when(storageService.store(any(), any()))
.then(returnsFirstArg());
doNothing()
.when(storageService).store(any(), any());

when(fileGateway.create(any()))
.thenThrow(new IllegalStateException("FileGateway Exception"));
Expand Down Expand Up @@ -479,7 +479,7 @@ void givenAValidParams_whenCallsExecuteAndFileGatewayCreateAndContentGatewayDele
assertEquals(expectedContentType, file.getContent().type());
assertNotNull(file.getCreatedAt());
assertNotNull(file.getUpdatedAt());
assertNotNull(file.getContent().location());
assertNotNull(file.getContent().storageKey());
assertEquals(file.getCreatedAt(), file.getUpdatedAt());

return true;
Expand Down Expand Up @@ -523,8 +523,9 @@ void givenAValidParams_whenCallsExecuteAndContentGatewayStoreThrowsRandomExcepti
when(folderGateway.findById(any()))
.thenReturn(Optional.of(folder));

when(storageService.store(any(), any()))
.thenThrow(new IllegalStateException("ContentGateway Exception"));
doThrow(new IllegalStateException("ContentGateway Exception"))
.when(storageService)
.store(any(), any());

final var input = CreateFileInput.of(
ownerId.getValue(),
Expand Down Expand Up @@ -709,8 +710,8 @@ void givenAnInvalidParamsWithContentTypeNull_whenCallsExecute_thenShouldThrowsVa
when(folderGateway.findById(any()))
.thenReturn(Optional.of(folder));

when(storageService.store(any(), any()))
.then(returnsFirstArg());
doNothing()
.when(storageService).store(any(), any());

final var input = CreateFileInput.of(
ownerId.getValue(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void givenAValidParam_whenCallsExecute_thenShouldDeleteFile() {
verify(fileGateway, times(1)).deleteById(any());
verify(fileGateway, times(1)).deleteById(eq(expectedFileId));
verify(storageService, times(1)).delete(any());
verify(storageService, times(1)).delete(eq(expectedContent.location()));
verify(storageService, times(1)).delete(eq(expectedContent.storageKey()));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.callv2.drive.domain.exception;

import java.util.List;

public class StorageKeyAlreadyExistsException extends SilentDomainException {

private StorageKeyAlreadyExistsException(final String key) {
super("storage key [%s] already exists.".formatted(key),
List.of(DomainException.Error.with("storage key [%s] already exists.".formatted(key))));
}

public static StorageKeyAlreadyExistsException with(final String key) {
return new StorageKeyAlreadyExistsException(key);
}

}
16 changes: 8 additions & 8 deletions domain/src/main/java/com/callv2/drive/domain/file/Content.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@
import com.callv2.drive.domain.validation.ValidationHandler;
import com.callv2.drive.domain.validation.ValidationError;

public record Content(String location, String type, long size) implements ValueObject {
public record Content(String storageKey, String type, long size) implements ValueObject {

public static Content of(final String location, final String type, final long size) {
return new Content(location, type, size);
public static Content of(final String storageKey, final String type, final long size) {
return new Content(storageKey, type, size);
}

@Override
public void validate(final ValidationHandler aHandler) {
validateLocation(aHandler);
validateStorageKey(aHandler);
validateType(aHandler);
}

private void validateLocation(final ValidationHandler aHandler) {
if (location == null) {
aHandler.append(new ValidationError("'location' cannot be null."));
private void validateStorageKey(final ValidationHandler aHandler) {
if (storageKey == null) {
aHandler.append(new ValidationError("'storageKey' cannot be null."));
return;
}

if (location.trim().isEmpty()) {
if (storageKey.trim().isEmpty()) {
aHandler.append(new ValidationError("'location' cannot be empty."));
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

public interface StorageService {

String store(String name, InputStream content);
void store(String key, InputStream content);

void delete(String name);
void delete(String key);

InputStream retrieve(String key);

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void givenAValidParams_whenCallsCreate_thenShouldCreateFile() {
assertEquals(expectedOwner, actualFile.getOwner());
assertEquals(expectedName, actualFile.getName().value());
assertEquals(expectedContentType, actualFile.getContent().type());
assertEquals(expectedContentLocation, actualFile.getContent().location());
assertEquals(expectedContentLocation, actualFile.getContent().storageKey());
assertEquals(expectedContentSize, actualFile.getContent().size());
assertNotNull(actualFile.getCreatedAt());
assertNotNull(actualFile.getUpdatedAt());
Expand Down Expand Up @@ -218,7 +218,7 @@ void givenAValidParams_whenCallsUpdate_thenShouldCreateFile() {

assertEquals(expectedName, actualUpdatedFile.getName());
assertEquals(expectedContentType, actualUpdatedFile.getContent().type());
assertEquals(expectedContentLocation, actualUpdatedFile.getContent().location());
assertEquals(expectedContentLocation, actualUpdatedFile.getContent().storageKey());
assertEquals(expectedContentSize, actualUpdatedFile.getContent().size());
assertNotNull(actualUpdatedFile.getCreatedAt());
assertNotNull(actualUpdatedFile.getUpdatedAt());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.util.List;
import java.util.UUID;

import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
Expand All @@ -17,8 +17,8 @@
import com.callv2.drive.application.file.content.get.GetFileContentOutput;
import com.callv2.drive.application.file.content.get.GetFileContentUseCase;
import com.callv2.drive.application.file.create.CreateFileUseCase;
import com.callv2.drive.application.file.delete.DeleteFileUseCase;
import com.callv2.drive.application.file.delete.DeleteFileInput;
import com.callv2.drive.application.file.delete.DeleteFileUseCase;
import com.callv2.drive.application.file.retrieve.get.GetFileInput;
import com.callv2.drive.application.file.retrieve.get.GetFileUseCase;
import com.callv2.drive.application.file.retrieve.list.ListFilesUseCase;
Expand Down Expand Up @@ -95,7 +95,7 @@ public ResponseEntity<Resource> download(UUID id) {
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + output.name() + "\"")
.contentLength(output.size())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new FileSystemResource(output.location()));
.body(new InputStreamResource(output.inputStream()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ GetFileUseCase getFileUseCase() {

@Bean
GetFileContentUseCase getFileContentUseCase() {
return new DefaultGetFileContentUseCase(fileGateway);
return new DefaultGetFileContentUseCase(fileGateway, storageService);
}

@Bean
Expand Down
Loading