package de.retest.recheck.persistence; import static de.retest.recheck.XmlTransformerUtil.getXmlTransformer; import static org.apache.commons.lang3.StringUtils.abbreviate; import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; import java.util.stream.Collectors; import de.retest.recheck.RecheckProperties; import de.retest.recheck.Rehub; import de.retest.recheck.persistence.bin.KryoPersistence; import de.retest.recheck.persistence.xml.XmlFolderPersistence; import de.retest.recheck.report.SuiteReplayResult; import de.retest.recheck.report.TestReport; import kong.unirest.HttpResponse; import kong.unirest.Unirest; import kong.unirest.UnirestException; import lombok.extern.slf4j.Slf4j; @Slf4j public class CloudPersistence<T extends Persistable> implements Persistence<T> { private static final int MAX_REPORT_NAME_LENGTH = 50; private static final String SERVICE_ENDPOINT = "https://marvin.prod.cloud.retest.org/api/report"; private final KryoPersistence<T> kryoPersistence = new KryoPersistence<>(); private final XmlFolderPersistence<T> folderPersistence = new XmlFolderPersistence<>( getXmlTransformer() ); public static final String RECHECK_API_KEY = "RECHECK_API_KEY"; @Override public void save( final URI identifier, final T element ) throws IOException { kryoPersistence.save( identifier, element ); if ( isAggregatedReport( identifier ) && element instanceof TestReport ) { final TestReport report = (TestReport) element; try { saveToCloud( report, Files.readAllBytes( Paths.get( identifier ) ) ); } catch ( final IOException e ) { if ( !report.containsChanges() ) { log.warn( "Could not read report '{}' for upload. Ignoring exception because the report does not have any differences.", identifier, e ); } else { log.error( "Could not read report '{}' for upload. Rethrowing because report has differences.", identifier, e ); throw e; } } } } private boolean isAggregatedReport( final URI identifier ) { return identifier.getPath().endsWith( RecheckProperties.AGGREGATED_TEST_REPORT_FILE_NAME ); } private List<String> getTestClasses( final TestReport report ) { return report.getSuiteReplayResults().stream() // .map( SuiteReplayResult::getName ) // .collect( Collectors.toList() ); } private void saveToCloud( final TestReport report, final byte[] data ) { final HttpResponse<String> uploadUrlResponse = getUploadUrl(); if ( uploadUrlResponse.isSuccess() ) { final ReportUploadContainer metadata = ReportUploadContainer.builder() // .reportName( String.join( ", ", getTestClasses( report ) ) ) // .data( data ) // .uploadUrl( uploadUrlResponse.getBody() ) // .build(); final boolean hasChanges = report.containsChanges(); final int maxAttempts = RecheckProperties.getInstance().rehubReportUploadAttempts(); for ( int remainingAttempts = maxAttempts - 1; remainingAttempts >= 0; remainingAttempts-- ) { try { uploadReport( metadata ); break; // Successful, abort retry } catch ( final UnirestException e ) { if ( !hasChanges ) { log.warn( "Failed to upload report. Ignoring exception because the report does not have any differences.", e ); break; } if ( remainingAttempts == 0 ) { log.error( "Failed to upload report. Aborting, because maximum retries have been reached. If this happens often, consider increasing the property '{}={}'.", RecheckProperties.REHUB_REPORT_UPLOAD_ATTEMPTS, maxAttempts, e ); throw e; } else { log.warn( "Failed to upload report. Retrying another {} times.", remainingAttempts, e ); } } } } } private void uploadReport( final ReportUploadContainer metadata ) { final long start = System.currentTimeMillis(); final HttpResponse<?> uploadResponse = Unirest.put( metadata.getUploadUrl() ) // .header( "x-amz-meta-report-name", abbreviate( metadata.getReportName(), MAX_REPORT_NAME_LENGTH ) ) // .body( metadata.getData() ) // .asEmpty(); if ( uploadResponse.isSuccess() ) { final long duration = System.currentTimeMillis() - start; log.info( "Successfully uploaded report to rehub in {} ms", duration ); } } private HttpResponse<String> getUploadUrl() { final String token = String.format( "Bearer %s", Rehub.getAccessToken() ); return Unirest.post( SERVICE_ENDPOINT ) // .header( "Authorization", token )// .asString(); } @Override public T load( final URI identifier ) throws IOException { if ( Paths.get( identifier ).toFile().isDirectory() ) { return folderPersistence.load( identifier ); } return kryoPersistence.load( identifier ); } }