Skip to content

Commit

Permalink
Merge pull request #897 from reportportal/rc/5.9.0
Browse files Browse the repository at this point in the history
Release 5.9.0
  • Loading branch information
IvanKustau committed Jun 30, 2023
2 parents dcbaca8 + 15bbe67 commit 3d79712
Show file tree
Hide file tree
Showing 30 changed files with 1,744 additions and 798 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ on:

env:
GH_USER_NAME: github.actor
SCRIPTS_VERSION: 5.7.0
SCRIPTS_VERSION: 5.8.0
BOM_VERSION: 5.7.5
MIGRATIONS_VERSION: 5.8.0
RELEASE_VERSION: 5.8.0
MIGRATIONS_VERSION: 5.9.0
RELEASE_VERSION: 5.9.0

jobs:
release:
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ repositories {

dependencyManagement {
imports {
mavenBom(releaseMode ? 'com.epam.reportportal:commons-bom:' + getProperty('bom.version') : 'com.github.reportportal:commons-bom:2014aa5')
mavenBom(releaseMode ? 'com.epam.reportportal:commons-bom:' + getProperty('bom.version') : 'com.github.reportportal:commons-bom:80a17605')
mavenBom('io.zonky.test.postgres:embedded-postgres-binaries-bom:12.9.0')
}
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=5.8.1
version=5.9.0
5 changes: 3 additions & 2 deletions project-properties.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ project.ext {
dependencyRepos = ["commons", "commons-rules", "commons-model", "commons-bom"]
releaseMode = project.hasProperty("releaseMode")
scriptsUrl = commonScriptsUrl + (releaseMode ? getProperty('scripts.version') : 'master')
migrationsUrl = migrationsScriptsUrl + (releaseMode ? getProperty('migrations.version') : 'hotfix/5.7.5')
migrationsUrl = migrationsScriptsUrl + (releaseMode ? getProperty('migrations.version') : 'master')
//TODO refactor with archive download
testScriptsSrc = [
(migrationsUrl + '/migrations/0_extensions.up.sql') : 'V001__extensions.sql',
Expand Down Expand Up @@ -67,7 +67,8 @@ project.ext {
(migrationsUrl + '/migrations/59_stale_materialized_view.up.sql') : 'V059__stale_materialized_view.sql',
(migrationsUrl + '/migrations/60_sender_case_operator.up.sql') : 'V060__sender_case_operator.sql',
(migrationsUrl + '/migrations/61_remove_acl.up.sql') : 'V061__remove_acl.sql',
(migrationsUrl + '/migrations/62_remove_dashboard_cascade_drop.up.sql') : 'V062_remove_dashboard_cascade_drop.sql',
(migrationsUrl + '/migrations/62_remove_dashboard_cascade_drop.up.sql') : 'V062__remove_dashboard_cascade_drop.sql',
(migrationsUrl + '/migrations/67_api_keys.up.sql') : 'V067__api_keys.sql',

]
excludeTests = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
import com.epam.ta.reportportal.entity.attachment.Attachment;
import com.epam.ta.reportportal.entity.attachment.AttachmentMetaInfo;
import com.epam.ta.reportportal.entity.attachment.BinaryData;
import com.epam.ta.reportportal.entity.enums.FeatureFlag;
import com.epam.ta.reportportal.exception.ReportPortalException;
import com.epam.ta.reportportal.filesystem.FilePathGenerator;
import com.epam.ta.reportportal.util.FeatureFlagHandler;
import com.epam.ta.reportportal.ws.model.ErrorType;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
Expand All @@ -53,125 +55,152 @@
@Service
public class AttachmentBinaryDataServiceImpl implements AttachmentBinaryDataService {

private static final Logger LOGGER = LoggerFactory.getLogger(AttachmentBinaryDataServiceImpl.class);

private final ContentTypeResolver contentTypeResolver;

private final FilePathGenerator filePathGenerator;

private final DataStoreService dataStoreService;

private final AttachmentRepository attachmentRepository;

private final CreateLogAttachmentService createLogAttachmentService;

@Autowired
public AttachmentBinaryDataServiceImpl(ContentTypeResolver contentTypeResolver, FilePathGenerator filePathGenerator,
@Qualifier("attachmentDataStoreService") DataStoreService dataStoreService, AttachmentRepository attachmentRepository,
CreateLogAttachmentService createLogAttachmentService) {
this.contentTypeResolver = contentTypeResolver;
this.filePathGenerator = filePathGenerator;
this.dataStoreService = dataStoreService;
this.attachmentRepository = attachmentRepository;
this.createLogAttachmentService = createLogAttachmentService;
}

@Override
public Optional<BinaryDataMetaInfo> saveAttachment(AttachmentMetaInfo metaInfo, MultipartFile file) {
Optional<BinaryDataMetaInfo> result = Optional.empty();
try (InputStream inputStream = file.getInputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
inputStream.transferTo(outputStream);
String contentType = resolveContentType(file.getContentType(), outputStream);
String fileName = resolveFileName(metaInfo, file, contentType);

String commonPath = filePathGenerator.generate(metaInfo);
String targetPath = Paths.get(commonPath, fileName).toString();

String fileId;
try (ByteArrayInputStream copy = new ByteArrayInputStream(outputStream.toByteArray())) {
fileId = dataStoreService.save(targetPath, copy);
}

result = Optional.of(BinaryDataMetaInfo.BinaryDataMetaInfoBuilder.aBinaryDataMetaInfo()
.withFileId(fileId)
.withContentType(contentType)
.withFileSize(file.getSize())
.build());
} catch (IOException e) {
LOGGER.error("Unable to save binary data", e);
} finally {
if (file instanceof CommonsMultipartFile) {
((CommonsMultipartFile) file).getFileItem().delete();
}
}
return result;
}

private String resolveFileName(AttachmentMetaInfo metaInfo, MultipartFile file, String contentType) {
String extension = resolveExtension(contentType).orElse(resolveExtension(true, file));
return metaInfo.getLogUuid() + "-" + file.getName() + extension;
}

@Override
public void saveFileAndAttachToLog(MultipartFile file, AttachmentMetaInfo attachmentMetaInfo) {
saveAttachment(attachmentMetaInfo, file).ifPresent(it -> attachToLog(it, attachmentMetaInfo));
}

@Override
public void attachToLog(BinaryDataMetaInfo binaryDataMetaInfo, AttachmentMetaInfo attachmentMetaInfo) {
try {
Attachment attachment = new Attachment();
attachment.setFileId(binaryDataMetaInfo.getFileId());
attachment.setThumbnailId(binaryDataMetaInfo.getThumbnailFileId());
attachment.setContentType(binaryDataMetaInfo.getContentType());
attachment.setFileSize(binaryDataMetaInfo.getFileSize());

attachment.setProjectId(attachmentMetaInfo.getProjectId());
attachment.setLaunchId(attachmentMetaInfo.getLaunchId());
attachment.setItemId(attachmentMetaInfo.getItemId());
attachment.setCreationDate(attachmentMetaInfo.getCreationDate());

createLogAttachmentService.create(attachment, attachmentMetaInfo.getLogId());
} catch (Exception exception) {
LOGGER.error("Cannot save log to database, remove files ", exception);

dataStoreService.delete(binaryDataMetaInfo.getFileId());
dataStoreService.delete(binaryDataMetaInfo.getThumbnailFileId());
throw exception;
}
}

@Override
public BinaryData load(Long fileId, ReportPortalUser.ProjectDetails projectDetails) {
try {
Attachment attachment = attachmentRepository.findById(fileId)
.orElseThrow(() -> new ReportPortalException(ErrorType.ATTACHMENT_NOT_FOUND, fileId));
InputStream data = dataStoreService.load(attachment.getFileId())
.orElseThrow(() -> new ReportPortalException(ErrorType.UNABLE_TO_LOAD_BINARY_DATA, fileId));
expect(attachment.getProjectId(), Predicate.isEqual(projectDetails.getProjectId())).verify(ErrorType.ACCESS_DENIED,
formattedSupplier("You are not assigned to project '{}'", projectDetails.getProjectName())
);
return new BinaryData(attachment.getContentType(), (long) data.available(), data);
} catch (IOException e) {
LOGGER.error("Unable to load binary data", e);
throw new ReportPortalException(ErrorType.UNCLASSIFIED_REPORT_PORTAL_ERROR, "Unable to load binary data");
}
}

@Override
public void delete(String fileId) {
if (StringUtils.isNotEmpty(fileId)) {
dataStoreService.delete(fileId);
attachmentRepository.findByFileId(fileId).ifPresent(attachmentRepository::delete);
}
}

private String resolveContentType(String contentType, ByteArrayOutputStream outputStream) throws IOException {
if (isContentTypePresent(contentType)) {
return contentType;
}
try (ByteArrayInputStream copy = new ByteArrayInputStream(outputStream.toByteArray())) {
return contentTypeResolver.detectContentType(copy);
}
}
private static final Logger LOGGER =
LoggerFactory.getLogger(AttachmentBinaryDataServiceImpl.class);

private final ContentTypeResolver contentTypeResolver;

private final FilePathGenerator filePathGenerator;

private final DataStoreService dataStoreService;

private final AttachmentRepository attachmentRepository;

private final CreateLogAttachmentService createLogAttachmentService;

private final FeatureFlagHandler featureFlagHandler;

/**
* Creates {@link AttachmentBinaryDataService}.
*
* @param contentTypeResolver {@link ContentTypeResolver}
* @param filePathGenerator {@link FilePathGenerator}
* @param dataStoreService {@link DataStoreService}
* @param attachmentRepository {@link AttachmentRepository}
* @param createLogAttachmentService {@link CreateLogAttachmentService}
* @param featureFlagHandler {@link FeatureFlagHandler}
*/
@Autowired
public AttachmentBinaryDataServiceImpl(ContentTypeResolver contentTypeResolver,
FilePathGenerator filePathGenerator,
@Qualifier("attachmentDataStoreService") DataStoreService dataStoreService,
AttachmentRepository attachmentRepository,
CreateLogAttachmentService createLogAttachmentService,
FeatureFlagHandler featureFlagHandler) {
this.contentTypeResolver = contentTypeResolver;
this.filePathGenerator = filePathGenerator;
this.dataStoreService = dataStoreService;
this.attachmentRepository = attachmentRepository;
this.createLogAttachmentService = createLogAttachmentService;
this.featureFlagHandler = featureFlagHandler;
}

@Override
public Optional<BinaryDataMetaInfo> saveAttachment(AttachmentMetaInfo metaInfo,
MultipartFile file) {
Optional<BinaryDataMetaInfo> result = Optional.empty();
try (InputStream inputStream = file.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
inputStream.transferTo(outputStream);
String contentType = resolveContentType(file.getContentType(), outputStream);
String fileName = resolveFileName(metaInfo, file, contentType);

String commonPath;
if (featureFlagHandler.isEnabled(FeatureFlag.SINGLE_BUCKET)) {
commonPath = Paths.get(PROJECT_PATH, filePathGenerator.generate(metaInfo)).toString();
} else {
commonPath = filePathGenerator.generate(metaInfo);
}
String targetPath = Paths.get(commonPath, fileName).toString();

String fileId;
try (ByteArrayInputStream copy = new ByteArrayInputStream(outputStream.toByteArray())) {
fileId = dataStoreService.save(targetPath, copy);
}

result = Optional.of(
BinaryDataMetaInfo.BinaryDataMetaInfoBuilder.aBinaryDataMetaInfo().withFileId(fileId)
.withContentType(contentType).withFileSize(file.getSize()).build());
} catch (IOException e) {
LOGGER.error("Unable to save binary data", e);
} finally {
if (file instanceof CommonsMultipartFile) {
((CommonsMultipartFile) file).getFileItem().delete();
}
}
return result;
}

private String resolveFileName(AttachmentMetaInfo metaInfo, MultipartFile file,
String contentType) {
String extension = resolveExtension(contentType).orElse(resolveExtension(true, file));
return metaInfo.getLogUuid() + "-" + file.getName() + extension;
}

@Override
public void saveFileAndAttachToLog(MultipartFile file, AttachmentMetaInfo attachmentMetaInfo) {
saveAttachment(attachmentMetaInfo, file).ifPresent(it -> attachToLog(it, attachmentMetaInfo));
}

@Override
public void attachToLog(BinaryDataMetaInfo binaryDataMetaInfo,
AttachmentMetaInfo attachmentMetaInfo) {
try {
Attachment attachment = new Attachment();
attachment.setFileId(binaryDataMetaInfo.getFileId());
attachment.setThumbnailId(binaryDataMetaInfo.getThumbnailFileId());
attachment.setContentType(binaryDataMetaInfo.getContentType());
attachment.setFileSize(binaryDataMetaInfo.getFileSize());

attachment.setProjectId(attachmentMetaInfo.getProjectId());
attachment.setLaunchId(attachmentMetaInfo.getLaunchId());
attachment.setItemId(attachmentMetaInfo.getItemId());
attachment.setCreationDate(attachmentMetaInfo.getCreationDate());

createLogAttachmentService.create(attachment, attachmentMetaInfo.getLogId());
} catch (Exception exception) {
LOGGER.error("Cannot save log to database, remove files ", exception);

dataStoreService.delete(binaryDataMetaInfo.getFileId());
dataStoreService.delete(binaryDataMetaInfo.getThumbnailFileId());
throw exception;
}
}

@Override
public BinaryData load(Long fileId, ReportPortalUser.ProjectDetails projectDetails) {
try {
Attachment attachment = attachmentRepository.findById(fileId)
.orElseThrow(() -> new ReportPortalException(ErrorType.ATTACHMENT_NOT_FOUND, fileId));
InputStream data = dataStoreService.load(attachment.getFileId()).orElseThrow(
() -> new ReportPortalException(ErrorType.UNABLE_TO_LOAD_BINARY_DATA, fileId));
expect(attachment.getProjectId(), Predicate.isEqual(projectDetails.getProjectId())).verify(
ErrorType.ACCESS_DENIED,
formattedSupplier("You are not assigned to project '{}'", projectDetails.getProjectName())
);
return new BinaryData(attachment.getContentType(), (long) data.available(), data);
} catch (IOException e) {
LOGGER.error("Unable to load binary data", e);
throw new ReportPortalException(
ErrorType.UNCLASSIFIED_REPORT_PORTAL_ERROR, "Unable to load binary data");
}
}

@Override
public void delete(String fileId) {
if (StringUtils.isNotEmpty(fileId)) {
dataStoreService.delete(fileId);
attachmentRepository.findByFileId(fileId).ifPresent(attachmentRepository::delete);
}
}

private String resolveContentType(String contentType, ByteArrayOutputStream outputStream)
throws IOException {
if (isContentTypePresent(contentType)) {
return contentType;
}
try (ByteArrayInputStream copy = new ByteArrayInputStream(outputStream.toByteArray())) {
return contentTypeResolver.detectContentType(copy);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,43 @@

package com.epam.ta.reportportal.binary.impl;

import static java.util.Optional.ofNullable;

import com.epam.ta.reportportal.binary.DataStoreService;
import com.epam.ta.reportportal.filesystem.DataEncoder;
import com.epam.ta.reportportal.filesystem.DataStore;

import java.io.InputStream;
import java.util.Optional;

import static java.util.Optional.ofNullable;

/**
* @author <a href="mailto:ihar_kahadouski@epam.com">Ihar Kahadouski</a>
*/
public abstract class CommonDataStoreService implements DataStoreService {

protected DataStore dataStore;
protected DataStore dataStore;

protected DataEncoder dataEncoder;
protected DataEncoder dataEncoder;

CommonDataStoreService(DataStore dataStore, DataEncoder dataEncoder) {
this.dataStore = dataStore;
this.dataEncoder = dataEncoder;
}
CommonDataStoreService(DataStore dataStore, DataEncoder dataEncoder) {
this.dataStore = dataStore;
this.dataEncoder = dataEncoder;
}

@Override
public String save(String fileName, InputStream data) {
return dataEncoder.encode(dataStore.save(fileName, data));
}
@Override
public String save(String fileName, InputStream data) {
return dataEncoder.encode(dataStore.save(fileName, data));
}

@Override
public abstract String saveThumbnail(String fileName, InputStream data);
@Override
public abstract String saveThumbnail(String fileName, InputStream data);

@Override
public void delete(String fileId) {
dataStore.delete(dataEncoder.decode(fileId));
}
@Override
public void delete(String fileId) {
dataStore.delete(dataEncoder.decode(fileId));
}

@Override
public Optional<InputStream> load(String fileId) {
return ofNullable(dataStore.load(dataEncoder.decode(fileId)));
}
@Override
public Optional<InputStream> load(String fileId) {
return ofNullable(dataStore.load(dataEncoder.decode(fileId)));
}
}
Loading

0 comments on commit 3d79712

Please sign in to comment.