package org.arquillian.cube.docker.impl.client.reporter; import io.restassured.RestAssured; import io.restassured.builder.ResponseBuilder; import io.restassured.filter.Filter; import io.restassured.filter.FilterContext; import io.restassured.filter.log.LogDetail; import io.restassured.internal.RestAssuredResponseImpl; import io.restassured.internal.print.RequestPrinter; import io.restassured.internal.print.ResponsePrinter; import io.restassured.response.Response; import io.restassured.specification.FilterableRequestSpecification; import io.restassured.specification.FilterableResponseSpecification; import org.arquillian.cube.docker.restassured.RestAssuredConfiguration; import org.arquillian.reporter.api.builder.Reporter; import org.arquillian.reporter.api.event.SectionEvent; import org.arquillian.reporter.api.model.entry.FileEntry; import org.arquillian.reporter.config.ReporterConfiguration; import org.jboss.arquillian.core.api.Event; import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.arquillian.core.api.annotation.Observes; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import static org.arquillian.cube.docker.impl.client.reporter.DockerEnvironmentReportKey.LOG_PATH; import static org.arquillian.cube.docker.impl.client.reporter.DockerEnvironmentReportKey.REST_REQUEST_LOG; import static org.arquillian.cube.docker.impl.client.reporter.DockerEnvironmentReportKey.REST_RESPONSE_LOG; public class TakeRestAssuredContent { @Inject Event<SectionEvent> reportEvent; // Execute after customizer (org.arquillian.cube.docker.restassured.RestAssuredCustomizer) public void registerFilterForRecorder(@Observes(precedence = -10) RestAssuredConfiguration restAssuredConfiguration, ReporterConfiguration reporterConfiguration) { RestAssured.filters(new TakeRequest(reportEvent, reporterConfiguration), new TakeResponse(reportEvent, reporterConfiguration)); } public static class TakeResponse implements Filter { private Event<SectionEvent> reportEvent; private ReporterConfiguration reporterConfiguration; public TakeResponse(Event<SectionEvent> reportEvent, ReporterConfiguration reporterConfiguration) { this.reportEvent = reportEvent; this.reporterConfiguration = reporterConfiguration; } @Override public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) { final Response response = ctx.next(requestSpec, responseSpec); final ByteArrayOutputStream responseLog = new ByteArrayOutputStream(); final PrintStream stream = new PrintStream(responseLog); ResponsePrinter.print(response, response, stream, LogDetail.ALL, true); final File logFile = new File(createLogDirectory(new File(reporterConfiguration.getRootDirectory())), "restassuredResponse.log"); writeContent(responseLog, logFile); final Path rootDir = Paths.get(reporterConfiguration.getRootDirectory()); final Path relativePath = rootDir.relativize(logFile.toPath()); Reporter.createReport(REST_RESPONSE_LOG) .addKeyValueEntry(LOG_PATH, new FileEntry(relativePath)) .inSection(new DockerLogSection()) .fire(reportEvent); final byte[] responseBody = response.asByteArray(); return cloneResponseIfNeeded(response, responseBody); } /* * If body expectations are defined we need to return a new Response otherwise the stream * has been closed due to the logging. */ private Response cloneResponseIfNeeded(Response response, byte[] responseAsString) { if (responseAsString != null && response instanceof RestAssuredResponseImpl && !((RestAssuredResponseImpl) response).getHasExpectations()) { final Response build = new ResponseBuilder().clone(response).setBody(responseAsString).build(); ((RestAssuredResponseImpl) build).setHasExpectations(true); return build; } return response; } } public static class TakeRequest implements Filter { private Event<SectionEvent> reportEvent; private ReporterConfiguration reporterConfiguration; public TakeRequest(Event<SectionEvent> reportEvent, ReporterConfiguration reporterConfiguration) { this.reportEvent = reportEvent; this.reporterConfiguration = reporterConfiguration; } @Override public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) { String uri = requestSpec.getURI(); uri = urlDecode(uri, Charset.forName(requestSpec.getConfig().getEncoderConfig().defaultQueryParameterCharset()), true); final ByteArrayOutputStream requestLog = new ByteArrayOutputStream(); final PrintStream stream = new PrintStream(requestLog); RequestPrinter.print(requestSpec, requestSpec.getMethod(), uri, LogDetail.ALL, stream, true); final File logFile = new File(createLogDirectory(new File(reporterConfiguration.getRootDirectory())), "restassuredRequest.log"); writeContent(requestLog, logFile); final Path rootDir = Paths.get(reporterConfiguration.getRootDirectory()); final Path relativePath = rootDir.relativize(logFile.toPath()); Reporter.createReport(REST_REQUEST_LOG) .addKeyValueEntry(LOG_PATH, new FileEntry(relativePath)) .inSection(new DockerLogSection()) .fire(reportEvent); return ctx.next(requestSpec, responseSpec); } private static String urlDecode(final String content, final Charset charset, final boolean plusAsBlank) { if (content == null) { return null; } final ByteBuffer bb = ByteBuffer.allocate(content.length()); final CharBuffer cb = CharBuffer.wrap(content); while (cb.hasRemaining()) { final char c = cb.get(); if (c == '%' && cb.remaining() >= 2) { final char uc = cb.get(); final char lc = cb.get(); final int u = Character.digit(uc, 16); final int l = Character.digit(lc, 16); if (u != -1 && l != -1) { bb.put((byte) ((u << 4) + l)); } else { bb.put((byte) '%'); bb.put((byte) uc); bb.put((byte) lc); } } else if (plusAsBlank && c == '+') { bb.put((byte) ' '); } else { bb.put((byte) c); } } bb.flip(); return charset.decode(bb).toString(); } } private static File createLogDirectory(File rootDirectory) { final Path reportsLogs = Paths.get("reports", "logs"); final Path logsDir = rootDirectory.toPath().resolve(reportsLogs); if (Files.notExists(logsDir)) { try { Files.createDirectories(logsDir); } catch (IOException e) { throw new IllegalArgumentException(String.format("Could not created logs directory at %s", logsDir)); } } return logsDir.toFile(); } private static void writeContent(ByteArrayOutputStream requestLog, File logFile) { try { Files.write(logFile.toPath(), requestLog.toByteArray()); } catch (IOException e) { throw new RuntimeException(e); } } }