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
Binary file added jars/plugin-robot-1.1.4.jar
Binary file not shown.
6 changes: 3 additions & 3 deletions plugin-robot/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ dependencyManagement {
ext['spring-boot.version'] = "${springBootVersion}"

dependencies {
implementation 'com.github.reportportal:commons-dao:5.15.6'
implementation 'com.github.reportportal:plugin-api:5.15.0'
annotationProcessor 'com.github.reportportal:plugin-api:5.15.0'
implementation 'com.github.reportportal:commons-dao:5.15.7'
implementation 'com.github.reportportal:plugin-api:5.15.1'
annotationProcessor 'com.github.reportportal:plugin-api:5.15.1'

implementation "org.springframework:spring-test"
// add lombok support
Expand Down
2 changes: 1 addition & 1 deletion plugin-robot/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=plugin-import-robot
version=1.1.3
version=1.1.4
description=Reinforce your ReportPortal instance with RobotFramework Import functionality and easily upload your log files right to ReportPortal.
pluginId=RobotFramework
lombokVersion=1.18.36
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import com.epam.ta.reportportal.dao.IntegrationTypeRepository;
import com.epam.ta.reportportal.dao.LaunchRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -55,7 +53,8 @@ public class RobotFrameworkPluginExtension implements ReportPortalExtensionPoint
this::getCommands);
private final String resourcesDir;
private final Supplier<ApplicationListener<PluginEvent>> pluginLoadedListener;
private final RequestEntityConverter requestEntityConverter;
private final Supplier<RequestEntityConverter> requestEntityConverterSupplier = new MemoizingSupplier<>(
this::createRequestEntityConverter);
@Autowired
private IntegrationTypeRepository integrationTypeRepository;
@Autowired
Expand All @@ -68,6 +67,8 @@ public class RobotFrameworkPluginExtension implements ReportPortalExtensionPoint
this::getCommonCommands);
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ObjectMapper objectMapper;

public RobotFrameworkPluginExtension(Map<String, Object> initParams) {
resourcesDir = IntegrationTypeProperties.RESOURCES_DIRECTORY.getValue(initParams)
Expand All @@ -77,11 +78,10 @@ public RobotFrameworkPluginExtension(Map<String, Object> initParams) {
new PluginEventHandlerFactory(resourcesDir, integrationTypeRepository,
integrationRepository)
));
}

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
requestEntityConverter = new RequestEntityConverter(objectMapper);
private RequestEntityConverter createRequestEntityConverter() {
return new RequestEntityConverter(objectMapper);
}

@PostConstruct
Expand Down Expand Up @@ -146,7 +146,7 @@ private Map<String, PluginCommand> getCommands() {

private Map<String, CommonPluginCommand<?>> getCommonCommands() {
HashMap<String, CommonPluginCommand<?>> pluginCommands = new HashMap<>();
var robotImportCommand = new RobotImportCommand(requestEntityConverter,
var robotImportCommand = new RobotImportCommand(requestEntityConverterSupplier.get(),
eventPublisher, launchRepository);
pluginCommands.put(robotImportCommand.getName(), robotImportCommand);
return pluginCommands;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@ public class RobotImportCommand implements CommonPluginCommand<StartLaunchRS> {

private final RequestEntityConverter requestEntityConverter;
private final ImportStrategyFactory importStrategyFactory;
private final LaunchRepository launchRepository;

public RobotImportCommand(RequestEntityConverter requestEntityConverter,
ApplicationEventPublisher eventPublisher,
LaunchRepository launchRepository) {
this.requestEntityConverter = requestEntityConverter;
this.launchRepository = launchRepository;
this.importStrategyFactory = new ImportStrategyFactory(eventPublisher, launchRepository);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
@Getter
public class LaunchImportRQ {

@JsonProperty
@JsonAlias({"launchUuid", "launch_uuid"})
private String launchUuid;

@JsonProperty(value = "name")
protected String name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.Optional;
import java.util.Set;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

/**
Expand Down Expand Up @@ -100,6 +101,20 @@ protected Boolean isSkippedNotIssue(Set<ItemAttributesRQ> attributes) {
.isPresent();
}

protected Boolean isSkippedNotIssue(LaunchImportRQ rq) {
return isSkippedNotIssue(ofNullable(rq).map(LaunchImportRQ::getAttributes).orElse(null));
}

protected Optional<String> getExistingLaunchUuid(LaunchImportRQ rq) {
return ofNullable(rq).map(LaunchImportRQ::getLaunchUuid).filter(StringUtils::hasText);
}

protected Instant getExistingLaunchStartTime(String launchUuid) {
return launchRepository.findByUuid(launchUuid)
.orElseThrow(() -> new ReportPortalException(ErrorType.LAUNCH_NOT_FOUND, launchUuid))
.getStartTime();
}

/*
* if the importing results do not contain initial timestamp a launch gets
* a default date if the launch is broken, time should be updated to not to broke
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,29 +102,33 @@ public class RobotXmlParser {
private final String projectName;
private final Deque<ItemInfo> items = new ArrayDeque<>();
private final boolean isSkippedNotIssue;
@Nullable
private final Instant launchStartTime;
private ZipFile zipFile;
private Instant lowestTime;
private Instant highestTime;

public RobotXmlParser(ApplicationEventPublisher eventPublisher, String launchUuid,
String projectName, boolean isSkippedNotIssue) {
this(eventPublisher, launchUuid, projectName, isSkippedNotIssue, null);
}

public RobotXmlParser(ApplicationEventPublisher eventPublisher, String launchUuid,
String projectName, boolean isSkippedNotIssue, @Nullable Instant launchStartTime) {
this.eventPublisher = eventPublisher;
this.launchUuid = launchUuid;
this.projectName = projectName;
this.lowestTime = Instant.now();
this.highestTime = Instant.EPOCH;
this.isSkippedNotIssue = isSkippedNotIssue;
this.launchStartTime = launchStartTime;
}

public RobotXmlParser(ApplicationEventPublisher eventPublisher, String launchUuid,
String projectName, ZipFile rootZipFile, boolean isSkippedNotIssue) {
this.eventPublisher = eventPublisher;
this.launchUuid = launchUuid;
this.projectName = projectName;
this.lowestTime = Instant.now();
this.highestTime = Instant.EPOCH;
String projectName, ZipFile rootZipFile, boolean isSkippedNotIssue,
@Nullable Instant launchStartTime) {
this(eventPublisher, launchUuid, projectName, isSkippedNotIssue, launchStartTime);
this.zipFile = rootZipFile;
this.isSkippedNotIssue = isSkippedNotIssue;
}

public void parse(InputStream inputStream) {
Expand All @@ -144,8 +148,7 @@ public void parse(InputStream inputStream) {
traverseNodes(root);

} catch (IOException | SAXException e) {
e.printStackTrace();
log.error(cleanMessage(e));
log.error("Failed to parse Robot XML report: {}", cleanMessage(e), e);
throw new ReportPortalException(ErrorType.UNCLASSIFIED_REPORT_PORTAL_ERROR, e);
}
}
Expand Down Expand Up @@ -283,7 +286,9 @@ private StartTestItemRQ buildStartItemRq(ItemInfo itemInfo) {
StartTestItemRQ rq = new StartTestItemRQ();
rq.setUuid(UUID.randomUUID().toString());
rq.setLaunchUuid(launchUuid);
rq.setStartTime(itemInfo.getStartTime());
Instant startTime = itemInfo.getStartTime();
validateItemTime(startTime);
rq.setStartTime(startTime);
rq.setHasStats(itemInfo.isHasStats());
rq.setType(itemInfo.getType().name());
rq.setDescription(itemInfo.getDescription());
Expand All @@ -300,15 +305,24 @@ private void finishTestItem() {
final FinishTestItemRQ rq = new FinishTestItemRQ();
markAsNotIssue(rq, itemInfo.getStatus());
rq.setStatus(itemInfo.getStatus().name());
rq.setEndTime(itemInfo.getEndTime());
Instant endTime = itemInfo.getEndTime();
rq.setEndTime(endTime);
rq.setLaunchUuid(launchUuid);
eventPublisher.publishEvent(new FinishItemRqEvent(this, projectName, itemInfo.getUuid(), rq));
if (itemInfo.getEndTime().isAfter(highestTime)) {
highestTime = itemInfo.getEndTime();
if (endTime.isAfter(highestTime)) {
highestTime = endTime;
}
}
}

private void validateItemTime(@Nullable Instant itemTime) {
if (launchStartTime != null && itemTime != null && itemTime.isBefore(launchStartTime)) {
throw new ReportPortalException(ErrorType.IMPORT_FILE_ERROR,
String.format("Item %s '%s' is earlier than launch start time '%s'", "start time", itemTime,
launchStartTime));
}
}

private void updateWithStatusInfo(Element element, ItemInfo itemInfo) {
findChildNodeByName(element, ATTR_STATUS.val()).ifPresent(status -> {
Element statusElement = (Element) status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
import com.epam.reportportal.rules.exception.ReportPortalException;
import com.epam.ta.reportportal.dao.LaunchRepository;
import java.io.InputStream;
import java.time.Instant;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -32,27 +35,40 @@
*/
public class XmlImportStrategy extends AbstractImportStrategy {

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

public XmlImportStrategy(ApplicationEventPublisher eventPublisher,
LaunchRepository launchRepository) {
super(eventPublisher, launchRepository);
}

@Override
public String importLaunch(MultipartFile file, String projectName, LaunchImportRQ rq) {
String launchUuid = UUID.randomUUID().toString();
var existingLaunchUuid = getExistingLaunchUuid(rq);
boolean importIntoExistingLaunch = existingLaunchUuid.isPresent();
String launchUuid = existingLaunchUuid.orElseGet(() -> UUID.randomUUID().toString());
Instant existingLaunchStartTime = importIntoExistingLaunch
? getExistingLaunchStartTime(launchUuid) : null;
try (InputStream xmlStream = file.getInputStream()) {
launchUuid = startLaunch(launchUuid, getLaunchName(file, XML_EXTENSION), projectName, rq);
if (!importIntoExistingLaunch) {
launchUuid = startLaunch(launchUuid, getLaunchName(file, XML_EXTENSION), projectName, rq);
}
RobotXmlParser robotXmlParser = new RobotXmlParser(eventPublisher, launchUuid,
projectName, isSkippedNotIssue(rq.getAttributes()));
projectName, isSkippedNotIssue(rq), existingLaunchStartTime);
if (!file.isEmpty()) {
robotXmlParser.parse(xmlStream);
}
finishLaunch(launchUuid, projectName, robotXmlParser.getHighestTime());
updateStartTime(launchUuid, robotXmlParser.getLowestTime());
if (!importIntoExistingLaunch) {
finishLaunch(launchUuid, projectName, robotXmlParser.getHighestTime());
updateStartTime(launchUuid, robotXmlParser.getLowestTime());
}
return launchUuid;
} catch (Exception e) {
e.printStackTrace();
updateBrokenLaunch(launchUuid);
LOGGER.error("Failed to import Robot XML file '{}' into launch '{}' for project '{}'",
file.getOriginalFilename(), launchUuid, projectName, e);
if (!importIntoExistingLaunch) {
updateBrokenLaunch(launchUuid);
}
throw new ReportPortalException(ErrorType.IMPORT_FILE_ERROR, cleanMessage(e));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.time.Instant;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -39,6 +42,8 @@
*/
public class ZipImportStrategy extends AbstractImportStrategy {

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

private static final Predicate<ZipEntry> isFile = zipEntry -> !zipEntry.isDirectory();
private static final Predicate<ZipEntry> isXml = zipEntry -> zipEntry.getName()
.endsWith(XML_EXTENSION);
Expand All @@ -55,28 +60,38 @@ public ZipImportStrategy(ApplicationEventPublisher eventPublisher,

@Override
public String importLaunch(MultipartFile file, String projectName, LaunchImportRQ rq) {
//copy of the launch's id to use it in catch block if something goes wrong
String launchUuid = UUID.randomUUID().toString();
var existingLaunchUuid = getExistingLaunchUuid(rq);
boolean importIntoExistingLaunch = existingLaunchUuid.isPresent();
String launchUuid = existingLaunchUuid.orElseGet(() -> UUID.randomUUID().toString());
Instant existingLaunchStartTime = importIntoExistingLaunch
? getExistingLaunchStartTime(launchUuid) : null;
File zip = transferToTempFile(file);

try (ZipFile zipFile = new ZipFile(zip)) {
launchUuid = startLaunch(launchUuid, getLaunchName(file, ZIP_EXTENSION), projectName, rq);
if (!importIntoExistingLaunch) {
launchUuid = startLaunch(launchUuid, getLaunchName(file, ZIP_EXTENSION), projectName, rq);
}
RobotXmlParser robotXmlParser = new RobotXmlParser(eventPublisher, launchUuid,
projectName, zipFile, isSkippedNotIssue(rq.getAttributes()));
projectName, zipFile, isSkippedNotIssue(rq), existingLaunchStartTime);
zipFile.stream().filter(isFile.and(isXml).and(isNotSystemDirectory))
.forEach(zipEntry -> robotXmlParser.parse(getEntryStream(zipFile, zipEntry)));
finishLaunch(launchUuid, projectName, robotXmlParser.getHighestTime());
updateStartTime(launchUuid, robotXmlParser.getLowestTime());
if (!importIntoExistingLaunch) {
finishLaunch(launchUuid, projectName, robotXmlParser.getHighestTime());
updateStartTime(launchUuid, robotXmlParser.getLowestTime());
}
return launchUuid;
} catch (Exception e) {
e.printStackTrace();
updateBrokenLaunch(launchUuid);
LOGGER.error("Failed to import Robot ZIP file '{}' into launch '{}' for project '{}'",
file.getOriginalFilename(), launchUuid, projectName, e);
if (!importIntoExistingLaunch) {
updateBrokenLaunch(launchUuid);
}
throw new ReportPortalException(ErrorType.IMPORT_FILE_ERROR, cleanMessage(e));
} finally {
try {
Files.deleteIfExists(zip.getAbsoluteFile().toPath());
} catch (IOException e) {
e.printStackTrace();
LOGGER.warn("Failed to delete temporary ZIP file '{}'", zip.getAbsolutePath(), e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DocumentBuilderInitializer {

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

public static DocumentBuilder get() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Expand All @@ -21,7 +25,7 @@ public static DocumentBuilder get() {
dbf.setXIncludeAware(false);
return dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
LOGGER.error("Failed to initialize secure XML document builder", e);
throw new ReportPortalException(ErrorType.PARSING_XML_ERROR, e);
}
}
Expand Down
Loading
Loading