package io.qameta.allure.bamboo; import com.atlassian.bamboo.plan.PlanResultKey; import com.atlassian.bamboo.resultsummary.ResultsSummaryManager; import com.atlassian.bamboo.servlet.BambooHttpServlet; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.atlassian.bamboo.plan.PlanKeys.getPlanResultKey; import static io.qameta.allure.bamboo.AllureBuildResult.fromCustomData; import static java.lang.Integer.parseInt; import static java.util.Optional.ofNullable; import static org.sonatype.aether.util.StringUtils.isEmpty; public class AllureReportServlet extends BambooHttpServlet { private static final Pattern URL_PATTERN = Pattern.compile(".*/plugins/servlet/allure/report/([^/]{2,})/([^/]+)/?(.*)"); private static final Logger LOGGER = LoggerFactory.getLogger(AllureReportServlet.class); private final AllureArtifactsManager artifactsManager; private final ResultsSummaryManager resultsSummaryManager; @Inject public AllureReportServlet(AllureArtifactsManager artifactsManager, ResultsSummaryManager resultsSummaryManager) { this.artifactsManager = artifactsManager; this.resultsSummaryManager = resultsSummaryManager; } public static Pattern getUrlPattern() { return URL_PATTERN; } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final Matcher matcher = URL_PATTERN.matcher(request.getRequestURI()); if (matcher.matches()) { response.setHeader("X-Frame-Options", "ALLOWALL"); final String planKeyString = matcher.group(1); final String buildNumber = matcher.group(2); final String filePath = matcher.group(3); final PlanResultKey planResultKey = getPlanResultKey(planKeyString, parseInt(buildNumber)); ofNullable(resultsSummaryManager.getResultsSummary(planResultKey)).ifPresent(results -> { final AllureBuildResult uploadResult = fromCustomData(results.getCustomBuildData()); if (uploadResult.isSuccess()) { artifactsManager.getArtifactUrl(planKeyString, buildNumber, filePath).ifPresent(file -> { try (final InputStream input = new URL(file).openStream()) { final Path path = Paths.get(file); response.setHeader("Content-Type", getServletContext().getMimeType(filePath)); response.setHeader("Content-Disposition", "inline; filename=\"" + path.getFileName().toString() + "\""); IOUtils.copy(input, response.getOutputStream()); } catch (IOException e) { LOGGER.error("Failed to send file {} of Allure Report ", file); } }); } else { final String errorMessage = isEmpty(uploadResult.getFailureDetails()) ? "Unknown error has occurred during Allure Build. Please refer the server logs for details." : "Something went wrong with Allure Report generation. Here are some details: \n" + uploadResult.getFailureDetails(); try { response.setHeader("Content-Type", "text/plain"); response.setHeader("Content-Length", String.valueOf(errorMessage.length())); response.setHeader("Content-Disposition", "inline"); response.getWriter().write(errorMessage); } catch (IOException e) { LOGGER.error("Failed to render error of Allure Report build ", e); } } }); } else { LOGGER.info("Path {} does not match pattern", request.getRequestURI()); } } }