BJ-323: update application logic

now application can to use cyrillic file names
fix tests logic
This commit is contained in:
a.karlov 2018-12-14 17:12:42 +03:00
parent 113769201c
commit f3489cca37
8 changed files with 197 additions and 140 deletions

View File

@ -1,2 +1,3 @@
# file-storage
file-storage service
Прокси, связывающий rbkmoney сервисы и ceph. Имплементирует Amazon S3 клиент, который используется, как клиент для подключения к ceph.
Ceph используется для сохранения файлов.

View File

@ -26,7 +26,7 @@
<dockerfile.base.service.tag>22c57470c4fc47161894f036b7cf9d70f42b75f5</dockerfile.base.service.tag>
<shared.resources.version>0.2.1</shared.resources.version>
<woody.thrift.version>1.1.15</woody.thrift.version>
<file.storage.proto.version>1.10-f44896e</file.storage.proto.version>
<file.storage.proto.version>1.14-b375587</file.storage.proto.version>
<geck.version>0.6.8</geck.version>
</properties>

View File

@ -18,7 +18,6 @@ import java.net.URL;
import java.time.Instant;
import java.util.Map;
import static com.rbkmoney.file.storage.util.CheckerUtil.checkFileName;
import static com.rbkmoney.file.storage.util.CheckerUtil.checkString;
@RequiredArgsConstructor
@ -28,14 +27,12 @@ public class FileStorageHandler implements FileStorageSrv.Iface {
private final StorageService storageService;
@Override
public NewFileResult createNewFile(String fileName, Map<String, Value> metadata, String expiresAt) throws TException {
public NewFileResult createNewFile(Map<String, Value> metadata, String expiresAt) throws TException {
try {
log.info("Request createNewFile fileName: {}, metadata: {}, expiresAt: {}", fileName, metadata, expiresAt);
checkString(fileName, "Bad request parameter, fileName required and not empty arg");
checkFileName(fileName, "Bad request parameter, enter the correct fileName");
log.info("Request createNewFile metadata: {}, expiresAt: {}", metadata, expiresAt);
// stringToInstant уже содержит проверки аргемента
Instant instant = TypeUtil.stringToInstant(expiresAt);
NewFileResult newFile = storageService.createNewFile(fileName, metadata, instant);
NewFileResult newFile = storageService.createNewFile(metadata, instant);
log.info("Response: newFileResult: {}", newFile);
return newFile;
} catch (StorageFileNotFoundException e) {
@ -51,14 +48,14 @@ public class FileStorageHandler implements FileStorageSrv.Iface {
}
@Override
public String generateDownloadUrl(String fileDataId, String expiresAt) throws TException {
public String generateDownloadUrl(String id, String expiresAt) throws TException {
try {
log.info("Request generateDownloadUrl fileDataId: {}, expiresAt: {}", fileDataId, expiresAt);
checkString(fileDataId, "Bad request parameter, fileDataId required and not empty arg");
log.info("Request generateDownloadUrl id: {}, expiresAt: {}", id, expiresAt);
checkString(id, "Bad request parameter, id required and not empty arg");
checkString(expiresAt, "Bad request parameter, expiresAt required and not empty arg");
// stringToInstant уже содержит проверки аргемента
Instant instant = TypeUtil.stringToInstant(expiresAt);
URL url = storageService.generateDownloadUrl(fileDataId, instant);
URL url = storageService.generateDownloadUrl(id, instant);
log.info("Response: url: {}", url);
return url.toString();
} catch (StorageFileNotFoundException e) {
@ -74,11 +71,11 @@ public class FileStorageHandler implements FileStorageSrv.Iface {
}
@Override
public FileData getFileData(String fileDataId) throws TException {
public FileData getFileData(String id) throws TException {
try {
log.info("Request getFileData fileDataId: {}", fileDataId);
checkString(fileDataId, "Bad request parameter, fileDataId required and not empty arg");
FileData fileData = storageService.getFileData(fileDataId);
log.info("Request getFileData id: {}", id);
checkString(id, "Bad request parameter, id required and not empty arg");
FileData fileData = storageService.getFileData(id);
log.info("Response: fileData: {}", fileData);
return fileData;
} catch (StorageFileNotFoundException e) {

View File

@ -6,12 +6,14 @@ import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.Upload;
import com.rbkmoney.damsel.msgpack.Value;
import com.rbkmoney.file.storage.FileData;
import com.rbkmoney.file.storage.NewFileResult;
import com.rbkmoney.file.storage.configuration.properties.StorageProperties;
import com.rbkmoney.file.storage.service.exception.StorageException;
import com.rbkmoney.file.storage.service.exception.StorageFileNotFoundException;
import com.rbkmoney.file.storage.util.DamselUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -30,11 +32,11 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class AmazonS3StorageService implements StorageService {
private static final String FILEDATA_FILE_ID = "x-rbkmoney-filedata-file-id";
private static final String FILEDATA_FILEDATA_ID = "x-rbkmoney-filedata-filedata-id";
private static final String FILEDATA_FILE_NAME = "x-rbkmoney-filedata-file-name";
private static final String FILEDATA_CREATED_AT = "x-rbkmoney-filedata-created-at";
private static final String FILEDATA_METADATA = "x-rbkmoney-filedata-metadata-";
private static final String ID = "x-rbkmoney-id";
private static final String FILE_ID = "x-rbkmoney-file-id";
private static final String CREATED_AT = "x-rbkmoney-created-at";
private static final String METADATA = "x-rbkmoney-metadata-";
private static final String FILENAME_PARAM = "filename=";
private final TransferManager transferManager;
private final AmazonS3 s3Client;
@ -51,43 +53,35 @@ public class AmazonS3StorageService implements StorageService {
}
@Override
public NewFileResult createNewFile(String fileName, Map<String, com.rbkmoney.damsel.msgpack.Value> metadata, Instant expirationTime) throws StorageException {
log.info("Trying to create new file to storage, filename='{}', bucketId='{}'", fileName, bucketName);
public NewFileResult createNewFile(Map<String, Value> metadata, Instant expirationTime) throws StorageException {
log.info("Trying to create new file to storage, bucketId='{}'", bucketName);
InputStream emptyContent = getEmptyContent();
String id = createId();
String fileId = createId();
FileDto fileDto = new FileDto(
id,
fileId,
Instant.now().toString(),
metadata
);
try {
InputStream emptyContent = getEmptyContent();
// в хранилище записывается неизменяемый фейковый файл с метаданными,
// в котором находится ссылка на реальный файл
uploadRequest(id, fileDto, emptyContent);
String fileDataId = getId();
String fileId = getId();
String createdAt = Instant.now().toString();
FileData fileData = new FileData(
fileDataId,
fileId,
fileName,
createdAt,
metadata
);
// записываем в хранилище пустой файл с метаданными по ключу fileDataId
uploadRequest(fileDataId, fileData, emptyContent);
// генерируем ссылку на запись файла в хранилище напрямую в цеф по ключу fileId
// генерируем ссылку на выгрузку файла в хранилище напрямую в цеф по ключу fileId
URL uploadUrl = generateUploadUrl(fileId, expirationTime);
log.info(
"File have been successfully created, fileId='{}', bucketId='{}', filename='{}'",
fileId,
bucketName,
fileName
);
log.info("File have been successfully created, id='{}', bucketId='{}'", id, bucketName);
return new NewFileResult(uploadUrl.toString(), fileData);
return new NewFileResult(id, uploadUrl.toString());
} catch (AmazonClientException ex) {
throw new StorageException(
String.format(
"Failed to create new file to storage, filename='%s', bucketId='%s'",
fileName,
"Failed to create new file to storage, id='%s', bucketId='%s'",
id,
bucketName
),
ex
@ -96,14 +90,36 @@ public class AmazonS3StorageService implements StorageService {
}
@Override
public URL generateDownloadUrl(String fileDataId, Instant expirationTime) throws StorageException {
String fileId = getValidFileData(fileDataId).getFileId();
public URL generateDownloadUrl(String id, Instant expirationTime) throws StorageException {
String fileId = getFileDto(id).getFileId();
// генерируем ссылку на загрузку файла из хранилища напрямую в цеф по ключу fileId
return generatePresignedUrl(fileId, expirationTime, HttpMethod.GET);
}
@Override
public FileData getFileData(String fileDataId) throws StorageException {
return getValidFileData(fileDataId);
public FileData getFileData(String id) throws StorageException {
FileDto fileDto = getFileDto(id);
S3Object object = getS3Object(fileDto.getFileId());
String fileName;
try {
String contentDisposition = object.getObjectMetadata().getContentDisposition();
int fileNameIndex = contentDisposition.lastIndexOf(FILENAME_PARAM) + FILENAME_PARAM.length();
fileName = contentDisposition.substring(fileNameIndex);
} catch (NullPointerException ex) {
throw new StorageException(
String.format(
"Failed to extract fileName, id='%s', bucketId='%s'",
id,
bucketName
),
ex
);
}
return new FileData(fileDto.getId(), fileName, fileDto.getCreatedAt(), fileDto.getMetadata());
}
@PreDestroy
@ -111,19 +127,19 @@ public class AmazonS3StorageService implements StorageService {
transferManager.shutdownNow(true);
}
private S3Object getS3Object(String fileDataId) throws StorageException {
private S3Object getS3Object(String id) throws StorageException {
try {
log.info(
"Trying to get file from storage, fileDataId='{}', bucketId='{}'",
fileDataId,
"Trying to get file from storage, id='{}', bucketId='{}'",
id,
bucketName
);
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, fileDataId);
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, id);
S3Object object = s3Client.getObject(getObjectRequest);
checkNotNull(object, fileDataId, "File");
checkNotNull(object, id, "File");
log.info(
"File have been successfully got from storage, fileDataId='{}', bucketId='{}'",
fileDataId,
"File have been successfully got from storage, id='{}', bucketId='{}'",
id,
bucketName
);
return object;
@ -131,7 +147,7 @@ public class AmazonS3StorageService implements StorageService {
throw new StorageException(
String.format(
"Failed to get file from storage, fileDataId='%s', bucketId='%s'",
fileDataId,
id,
bucketName
),
ex
@ -139,47 +155,45 @@ public class AmazonS3StorageService implements StorageService {
}
}
private void checkFileStatus(S3Object s3Object) throws StorageFileNotFoundException {
log.info("Check file expiration and uploaded status: ETag='{}'", s3Object.getObjectMetadata().getETag());
private void checkRealFileStatus(S3Object s3Object) throws StorageFileNotFoundException {
log.info("Check real file expiration and uploaded status: ETag='{}'", s3Object.getObjectMetadata().getETag());
ObjectMetadata objectMetadata = s3Object.getObjectMetadata();
String fileId = getFileIdFromObjectMetadata(objectMetadata);
if (s3Client.doesObjectExist(bucketName, fileId)) {
log.info("File was uploaded: ETag='{}'", s3Object.getObjectMetadata().getETag());
log.info("Real file was uploaded: ETag='{}'", s3Object.getObjectMetadata().getETag());
return;
}
// если файл не соотвествует условиям, блокируем доступ к нему
throw new StorageFileNotFoundException(String.format("File not found: fileId='%s', bucketId='%s', create a new file", s3Object.getKey(), bucketName));
throw new StorageFileNotFoundException(String.format("Real file not found: id='%s', bucketId='%s', create a new file", s3Object.getKey(), bucketName));
}
private FileData extractFileData(ObjectMetadata objectMetadata) {
log.info("Trying to extract metadata from storage: ETag='{}'", objectMetadata.getETag());
String fileId = getUserMetadataParameter(objectMetadata, FILEDATA_FILE_ID);
String fileDataId = getUserMetadataParameter(objectMetadata, FILEDATA_FILEDATA_ID);
String fileName = getUserMetadataParameter(objectMetadata, FILEDATA_FILE_NAME);
String createdAt = getUserMetadataParameter(objectMetadata, FILEDATA_CREATED_AT);
private FileDto getFileDtoByFakeFile(ObjectMetadata objectMetadata) {
log.info("Trying to extract real file metadata by fake file from storage: ETag='{}'", objectMetadata.getETag());
String id = getUserMetadataParameter(objectMetadata, ID);
String fileId = getFileIdFromObjectMetadata(objectMetadata);
String createdAt = getUserMetadataParameter(objectMetadata, CREATED_AT);
Map<String, com.rbkmoney.damsel.msgpack.Value> metadata = objectMetadata.getUserMetadata().entrySet().stream()
.filter(entry -> entry.getKey().startsWith(FILEDATA_METADATA) && entry.getValue() != null)
.filter(entry -> entry.getKey().startsWith(METADATA) && entry.getValue() != null)
.collect(
Collectors.toMap(
o -> o.getKey().substring(FILEDATA_METADATA.length()),
o -> o.getKey().substring(METADATA.length()),
o -> DamselUtil.fromJson(o.getValue(), com.rbkmoney.damsel.msgpack.Value.class)
)
);
log.info(
"Metadata have been successfully extracted from storage, fileId='{}', bucketId='{}'",
fileId,
"Real file metadata have been successfully extracted by fake file from storage, id='{}', bucketId='{}'",
id,
bucketName
);
return new FileData(fileDataId, fileId, fileName, createdAt, metadata);
return new FileDto(id, fileId, createdAt, metadata);
}
private URL generatePresignedUrl(String fileId, Instant expirationTime, HttpMethod httpMethod) throws StorageException {
try {
log.info(
"Trying to generate presigned url, fileId='{}', bucketId='{}', expirationTime='{}', httpMethod='{}'",
"Trying to generate presigned url for real file, fileId='{}', bucketId='{}', expirationTime='{}', httpMethod='{}'",
fileId,
bucketName,
expirationTime,
@ -218,8 +232,8 @@ public class AmazonS3StorageService implements StorageService {
return new ByteArrayInputStream(new byte[0]);
}
private void uploadRequest(String id, FileData fileData, InputStream inputStream) throws AmazonClientException {
PutObjectRequest putObjectRequest = createS3Request(id, fileData, inputStream);
private void uploadRequest(String id, FileDto fileDto, InputStream inputStream) throws AmazonClientException {
PutObjectRequest putObjectRequest = createS3Request(id, fileDto, inputStream);
Upload upload = transferManager.upload(putObjectRequest);
try {
@ -229,33 +243,32 @@ public class AmazonS3StorageService implements StorageService {
}
}
private PutObjectRequest createS3Request(String id, FileData fileData, InputStream inputStream) {
private PutObjectRequest createS3Request(String id, FileDto fileDto, InputStream inputStream) {
return new PutObjectRequest(
bucketName,
id,
inputStream,
createObjectMetadata(fileData)
createObjectMetadata(fileDto)
);
}
private ObjectMetadata createObjectMetadata(FileData fileData) {
private ObjectMetadata createObjectMetadata(FileDto fileDto) {
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentDisposition("attachment;filename=" + fileData.getFileName());
// filedata parameters
objectMetadata.addUserMetadata(FILEDATA_FILEDATA_ID, fileData.getFiledataId());
objectMetadata.addUserMetadata(FILEDATA_FILE_ID, fileData.getFileId());
objectMetadata.addUserMetadata(FILEDATA_FILE_NAME, fileData.getFileName());
objectMetadata.addUserMetadata(FILEDATA_CREATED_AT, fileData.getCreatedAt());
fileData.getMetadata().forEach(
(key, value) -> objectMetadata.addUserMetadata(FILEDATA_METADATA + key, DamselUtil.toJsonString(value))
objectMetadata.addUserMetadata(ID, fileDto.getId());
objectMetadata.addUserMetadata(FILE_ID, fileDto.getFileId());
objectMetadata.addUserMetadata(CREATED_AT, fileDto.getCreatedAt());
fileDto.getMetadata().forEach(
(key, value) -> objectMetadata.addUserMetadata(METADATA + key, DamselUtil.toJsonString(value))
);
return objectMetadata;
}
private FileData getValidFileData(String fileDataId) throws StorageException {
S3Object s3Object = getS3Object(fileDataId);
checkFileStatus(s3Object);
return extractFileData(s3Object.getObjectMetadata());
private FileDto getFileDto(String id) throws StorageException {
// извлечение фейкового файла с метаданными о реальном файле
S3Object s3Object = getS3Object(id);
// функция возвращает метаданные только в случае, если реальный файл был уже выгружен в хранилище
checkRealFileStatus(s3Object);
return getFileDtoByFakeFile(s3Object.getObjectMetadata());
}
private URL generateUploadUrl(String fileId, Instant expirationTime) throws StorageException {
@ -263,7 +276,7 @@ public class AmazonS3StorageService implements StorageService {
}
private String getFileIdFromObjectMetadata(ObjectMetadata objectMetadata) {
return getUserMetadataParameter(objectMetadata, FILEDATA_FILE_ID);
return getUserMetadataParameter(objectMetadata, FILE_ID);
}
private String getUserMetadataParameter(ObjectMetadata objectMetadata, String key) throws StorageException {
@ -271,7 +284,7 @@ public class AmazonS3StorageService implements StorageService {
.orElseThrow(() -> new StorageException("Failed to extract user metadata parameter, " + key + " is null"));
}
private String getId() {
private String createId() {
return UUID.randomUUID().toString();
}
@ -280,4 +293,15 @@ public class AmazonS3StorageService implements StorageService {
throw new StorageFileNotFoundException(String.format(objectType + " is null, fileId='%s', bucketId='%s'", fileId, bucketName));
}
}
@RequiredArgsConstructor
@Getter
private class FileDto {
private final String id;
private final String fileId;
private final String createdAt;
private final Map<String, Value> metadata;
}
}

View File

@ -11,10 +11,10 @@ import java.util.Map;
public interface StorageService {
NewFileResult createNewFile(String fileName, Map<String, Value> metadata, Instant expirationTime) throws StorageException;
NewFileResult createNewFile(Map<String, Value> metadata, Instant expirationTime) throws StorageException;
URL generateDownloadUrl(String fileDataId, Instant expirationTime) throws StorageException;
URL generateDownloadUrl(String id, Instant expirationTime) throws StorageException;
FileData getFileData(String fileDataId) throws StorageException;
FileData getFileData(String id) throws StorageException;
}

View File

@ -2,11 +2,22 @@ server.port=@server.port@
spring.application.name=@project.name@
info.version=@project.version@
info.stage=dev
# --
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.enabled=true
# --
storage.endpoint=localhost
storage.signingRegion=RU
storage.clientProtocol=HTTP
storage.clientMaxErrorRetry=10
storage.bucketName="files"
# ----
# for up local test version
#storage.endpoint=localhost:32827
#storage.signingRegion=RU
#storage.accessKey=test
#storage.secretKey=test
#storage.clientProtocol=HTTP
#storage.clientMaxErrorRetry=10
#storage.bucketName=TEST

View File

@ -16,10 +16,8 @@ import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
@ -46,6 +44,7 @@ public abstract class AbstractIntegrationTest {
protected FileStorageSrv.Iface client;
// for up local test version comment this ClassRule
@ClassRule
public static GenericContainer cephContainer = new GenericContainer("dr.rbkmoney.com/ceph-demo:latest")
.withEnv("RGW_NAME", "localhost")
@ -66,8 +65,9 @@ public abstract class AbstractIntegrationTest {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
// for up local test version comment this row
"storage.endpoint=" + cephContainer.getContainerIpAddress() + ":" + cephContainer.getMappedPort(80),
// в случае, если поднят локальный сторедж в контейнере
// for up local test version uncomment this row
// "storage.endpoint=localhost:32827",
"storage.signingRegion=" + SIGNING_REGION,
"storage.accessKey=" + AWS_ACCESS_KEY,
@ -100,11 +100,17 @@ public abstract class AbstractIntegrationTest {
return ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now());
}
protected HttpURLConnection getHttpURLConnection(URL url, boolean doOutput, String method) throws IOException {
protected HttpURLConnection getHttpURLConnection(URL url, String method, boolean doOutput) throws IOException {
return getHttpURLConnection(url, method, null, doOutput);
}
protected HttpURLConnection getHttpURLConnection(URL url, String method, String fileName, boolean doOutput) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(doOutput);
connection.setRequestMethod(method);
if (fileName != null) {
connection.setRequestProperty("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
}
return connection;
}
}

View File

@ -10,6 +10,8 @@ import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@ -24,7 +26,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
// все тесты в 1 классе , чтобы сэкономить время на поднятии тест контейнера
public class FileStorageTest extends AbstractIntegrationTest {
private static final String TEST_DATA = "test";
private static final String FILE_DATA = "test";
private static final String FILE_NAME = "rainbow-champion";
@Test
public void uploadAndDownloadFileFromStorageTest() throws IOException, TException {
@ -35,13 +38,13 @@ public class FileStorageTest extends AbstractIntegrationTest {
try {
// создание нового файла
String expirationTime = generateCurrentTimePlusDay().toString();
NewFileResult fileResult = client.createNewFile("test_file", Collections.emptyMap(), expirationTime);
uploadTestData(fileResult);
NewFileResult fileResult = client.createNewFile(Collections.emptyMap(), expirationTime);
uploadTestData(fileResult, FILE_NAME, FILE_DATA);
// генерация url с доступом только для загрузки
URL downloadUrl = new URL(client.generateDownloadUrl(fileResult.getFileData().getFiledataId(), expirationTime));
URL downloadUrl = new URL(client.generateDownloadUrl(fileResult.getId(), expirationTime));
HttpURLConnection downloadUrlConnection = getHttpURLConnection(downloadUrl, false, "GET");
HttpURLConnection downloadUrlConnection = getHttpURLConnection(downloadUrl, "GET", false);
InputStream inputStream = downloadUrlConnection.getInputStream();
// чтение записанного файла из хранилища
@ -51,7 +54,7 @@ public class FileStorageTest extends AbstractIntegrationTest {
assertEquals(Files.readAllLines(testFile), Collections.emptyList());
// запись тестовых данных в пустой testFile
Files.write(testFile, TEST_DATA.getBytes());
Files.write(testFile, FILE_DATA.getBytes());
assertEquals(Files.readAllLines(testFile), Files.readAllLines(testActualFile));
} finally {
Files.deleteIfExists(testFile);
@ -63,9 +66,9 @@ public class FileStorageTest extends AbstractIntegrationTest {
public void accessErrorToMethodsTest() throws IOException, TException {
// создание файла с доступом к файлу на день
String expirationTime = generateCurrentTimePlusDay().toString();
NewFileResult fileResult = client.createNewFile("test_file", Collections.emptyMap(), expirationTime);
NewFileResult fileResult = client.createNewFile(Collections.emptyMap(), expirationTime);
String fileDataId = fileResult.getFileData().getFiledataId();
String fileDataId = fileResult.getId();
// ошибка доступа - файла не существует, тк не было upload
assertThrows(FileNotFound.class, () -> client.generateDownloadUrl(fileDataId, expirationTime));
@ -76,53 +79,53 @@ public class FileStorageTest extends AbstractIntegrationTest {
public void uploadUrlConnectionAccessTest() throws IOException, TException {
// создание файла с доступом к файлу на день
String expirationTime = generateCurrentTimePlusDay().toString();
NewFileResult fileResult = client.createNewFile("test_file", Collections.emptyMap(), expirationTime);
NewFileResult fileResult = client.createNewFile(Collections.emptyMap(), expirationTime);
URL uploadUrl = new URL(fileResult.getUploadUrl());
// ошибка при запросе по url методом get
assertEquals(HttpStatus.FORBIDDEN.value(), getHttpURLConnection(uploadUrl, false, "GET").getResponseCode());
assertEquals(HttpStatus.FORBIDDEN.value(), getHttpURLConnection(uploadUrl, "GET", false).getResponseCode());
// Length Required при запросе по url методом put
assertEquals(HttpStatus.LENGTH_REQUIRED.value(), getHttpURLConnection(uploadUrl, true, "PUT").getResponseCode());
assertEquals(HttpStatus.LENGTH_REQUIRED.value(), getHttpURLConnection(uploadUrl, "PUT", FILE_NAME, true).getResponseCode());
uploadTestData(fileResult);
uploadTestData(fileResult, FILE_NAME, FILE_DATA);
}
@Test
public void downloadUrlConnectionAccessTest() throws IOException, TException {
// создание файла с доступом к файлу на день
String expirationTime = generateCurrentTimePlusDay().toString();
NewFileResult fileResult = client.createNewFile("test_file", Collections.emptyMap(), expirationTime);
NewFileResult fileResult = client.createNewFile(Collections.emptyMap(), expirationTime);
String fileDataId = fileResult.getFileData().getFiledataId();
String fileDataId = fileResult.getId();
// upload тестовых данных в хранилище
uploadTestData(fileResult);
uploadTestData(fileResult, FILE_NAME, FILE_DATA);
// генерация url с доступом только для загрузки
URL url = new URL(client.generateDownloadUrl(fileDataId, expirationTime));
// с данной ссылкой нельзя записывать
assertEquals(HttpStatus.FORBIDDEN.value(), getHttpURLConnection(url, true, "PUT").getResponseCode());
assertEquals(HttpStatus.FORBIDDEN.value(), getHttpURLConnection(url, "PUT", FILE_NAME, true).getResponseCode());
// можно читать
assertEquals(HttpStatus.OK.value(), getHttpURLConnection(url, false, "GET").getResponseCode());
assertEquals(HttpStatus.OK.value(), getHttpURLConnection(url, "GET", false).getResponseCode());
}
@Test
public void expirationTimeTest() throws TException, InterruptedException, IOException {
// создание файла с доступом к файлу на день
String expirationTime = generateCurrentTimePlusDay().toString();
NewFileResult validFileResult = client.createNewFile("test_file", Collections.emptyMap(), expirationTime);
NewFileResult validFileResult = client.createNewFile(Collections.emptyMap(), expirationTime);
String validFileDataId = validFileResult.getFileData().getFiledataId();
String validFileDataId = validFileResult.getId();
// задержка перед upload для теста expiration
Thread.sleep(1000);
// сохранение тестовых данных в хранилище
uploadTestData(validFileResult);
uploadTestData(validFileResult, FILE_NAME, FILE_DATA);
// доступ есть
client.getFileData(validFileDataId);
@ -130,9 +133,9 @@ public class FileStorageTest extends AbstractIntegrationTest {
// - - - - - сделаем задержку больше expiration
// создание файла с доступом к файлу на секунду
NewFileResult throwingFileResult = client.createNewFile("test_file", Collections.emptyMap(), generateCurrentTimePlusSecond().toString());
NewFileResult throwingFileResult = client.createNewFile(Collections.emptyMap(), generateCurrentTimePlusSecond().toString());
String throwingFileDataId = throwingFileResult.getFileData().getFiledataId();
String throwingFileDataId = throwingFileResult.getId();
// ошибка доступа - файла не существует, тк не было upload
assertThrows(FileNotFound.class, () -> client.generateDownloadUrl(throwingFileDataId, expirationTime));
@ -142,7 +145,7 @@ public class FileStorageTest extends AbstractIntegrationTest {
Thread.sleep(2000);
// сохранение тестовых данных в хранилище вызывает ошибку доступа
assertThrows(AssertionError.class, () -> uploadTestData(throwingFileResult));
assertThrows(AssertionError.class, () -> uploadTestData(throwingFileResult, FILE_NAME, FILE_DATA));
// ошибка доступа
assertThrows(FileNotFound.class, () -> client.getFileData(throwingFileDataId));
@ -152,33 +155,48 @@ public class FileStorageTest extends AbstractIntegrationTest {
@Test
public void extractMetadataTest() throws TException, IOException {
String expirationTime = generateCurrentTimePlusDay().toString();
String fileName = "test_file";
Map<String, Value> metadata = new HashMap<String, Value>() {{
put("key1", Value.b(true));
put("key2", Value.i(1));
put("key3", Value.flt(1));
put("key4", Value.arr(new ArrayList<>()));
put("key5", Value.str(TEST_DATA));
put("key5", Value.str(FILE_DATA));
put("key6", Value.bin(new byte[]{}));
}};
NewFileResult fileResult = client.createNewFile(fileName, metadata, expirationTime);
uploadTestData(fileResult);
assertEquals(fileResult.getFileData().getFileName(), fileName);
NewFileResult fileResult = client.createNewFile(metadata, expirationTime);
uploadTestData(fileResult, FILE_NAME, FILE_DATA);
FileData fileData = client.getFileData(fileResult.getFileData().getFiledataId());
FileData fileData = client.getFileData(fileResult.getId());
assertEquals(fileData, fileResult.getFileData());
assertEquals(fileData.getMetadata(), metadata);
}
private void uploadTestData(final NewFileResult fileResult) throws IOException {
@Test
public void fileNameCyrillicTest() throws TException, IOException {
// создание файла с доступом к файлу на день
String expirationTime = generateCurrentTimePlusDay().toString();
NewFileResult fileResult = client.createNewFile(Collections.emptyMap(), expirationTime);
// upload тестовых данных в хранилище
String fileName = "csgo-лучше-чем-1.6";
uploadTestData(fileResult, fileName, FILE_DATA);
FileData fileData = client.getFileData(fileResult.getId());
// тут используется энкодер\декодер, потому что apache http клиент менять кодировку.
// при аплоаде напрямую по uploadUrl в ceph такой проблемы нет
assertEquals(fileName, URLDecoder.decode(fileData.getFileName(), StandardCharsets.UTF_8.name()));
}
private void uploadTestData(NewFileResult fileResult, String fileName, String testData) throws IOException {
// запись данных методом put
URL uploadUrl = new URL(fileResult.getUploadUrl());
HttpURLConnection uploadUrlConnection = getHttpURLConnection(uploadUrl, true, "PUT");
HttpURLConnection uploadUrlConnection = getHttpURLConnection(uploadUrl, "PUT", fileName, true);
OutputStreamWriter out = new OutputStreamWriter(uploadUrlConnection.getOutputStream());
out.write(TEST_DATA);
out.write(testData);
out.close();
// чтобы завершить загрузку вызываем getResponseCode