package org.carlspring.strongbox.rest.client; import org.carlspring.strongbox.client.ArtifactOperationException; import org.carlspring.strongbox.client.ArtifactTransportException; import org.carlspring.strongbox.client.BaseArtifactClient; import org.carlspring.strongbox.client.IArtifactClient; import javax.xml.bind.JAXBException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import com.google.common.io.ByteStreams; import com.jayway.restassured.http.ContentType; import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc; import com.jayway.restassured.module.mockmvc.response.MockMvcResponse; import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification; import com.jayway.restassured.response.ExtractableResponse; import com.jayway.restassured.response.Headers; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; /** * Implementation of {@link IArtifactClient} for rest-assured tests. * * @author Alex Oreshkevich */ @Component public class RestAssuredArtifactClient extends BaseArtifactClient { /** * Default validation policy for GET requests. */ public final static boolean VALIDATE_RESOURCE_ON_GET = false; public final static int OK = HttpStatus.OK.value(); public final static int PARTIAL_CONTENT = HttpStatus.PARTIAL_CONTENT.value(); private String contextBaseUrl; @Override public String getContextBaseUrl() { return contextBaseUrl; } public void setContextBaseUrl(String contextBaseUrl) { this.contextBaseUrl = contextBaseUrl; } @Override public boolean pathExists(String path) { String url = escapeUrl(path); logger.debug("Path to artifact: " + url); return givenLocal().contentType(ContentType.TEXT) .when() .get(url) .getStatusCode() == OK; } @Override public InputStream getResource(String path, long offset) throws ArtifactTransportException, IOException { return getResource(path, (int) offset); } public void deployFile(InputStream is, String url, String fileName) throws ArtifactOperationException { put(is, url, fileName, ContentType.BINARY.toString()); } public void deployMetadata(InputStream is, String url, String fileName) throws ArtifactOperationException { put(is, url, fileName, ContentType.BINARY.toString()); } @Override public void put(InputStream is, String url, String fileName, String mediaType) throws ArtifactOperationException { String contentDisposition = "attachment; filename=\"" + fileName + "\""; byte[] bytes; try { bytes = ByteStreams.toByteArray(is); } catch (IOException e) { throw new ArtifactOperationException("Unable to convert to byte array", e); } logger.debug("Deploying " + url); givenLocal().contentType(mediaType) .header("Content-Disposition", contentDisposition) .header("filename", fileName) .body(bytes) .when() .put(url) .peek() .then() .statusCode(HttpStatus.OK.value()); } private MockMvcRequestSpecification givenLocal() { return RestAssuredMockMvc.given().header("User-Agent", "Maven/*"); } public MockMvcResponse put2(String relativeUrl, Object body, String mediaType) { return givenLocal().contentType(mediaType).body(body).when().put(relativeUrl).peek(); } public InputStream getResource(String url) { return getResource(url, -1, VALIDATE_RESOURCE_ON_GET); } public InputStream getResource(String url, boolean validate) { return getResource(url, -1, validate); } public InputStream getResource(String url, int offset, boolean validate) { byte[] bytes = getArtifactAsByteArray(url, offset, validate); if (bytes == null) { return null; } else { return new ByteArrayInputStream(bytes); } } public InputStream getResource(String url, int offset) { return getResource(url, offset, VALIDATE_RESOURCE_ON_GET); } /** * Converts response output to byte array to properly use it later as a stream. * RestAssured-specific case for working with file uploading when multipart specification is not used. */ public byte[] getArtifactAsByteArray(String url, int offset, boolean validate) { MockMvcRequestSpecification o = givenLocal().contentType(MediaType.TEXT_PLAIN_VALUE); int statusCode = OK; if (offset != -1) { o = o.header("Range", "bytes=" + offset + "-"); statusCode = PARTIAL_CONTENT; } logger.debug("[getArtifactAsByteArray] URL " + url); MockMvcResponse response = o.when().get(url); Headers allHeaders = response.getHeaders(); logger.debug("HTTP GET " + url); logger.debug("Response headers:"); allHeaders.forEach(header -> logger.debug("\t" + header.getName() + " = " + header.getValue())); if (validate) { response.then().statusCode(statusCode); } if (response.getStatusCode() == OK || response.getStatusCode() == PARTIAL_CONTENT) { byte[] result = response.getMockHttpServletResponse().getContentAsByteArray(); logger.debug("Received " + result.length + " bytes."); return result; } else { logger.warn("[getArtifactAsByteArray] response " + response.getStatusCode()); return null; } } public void copy(String path, String srcStorageId, String srcRepositoryId, String destStorageId, String destRepositoryId) { givenLocal().contentType(MediaType.TEXT_PLAIN_VALUE) .params("srcStorageId", srcStorageId, "srcRepositoryId", srcRepositoryId, "destStorageId", destStorageId, "destRepositoryId", destRepositoryId) .when() .post(getContextBaseUrl() + "/storages/copy/" + path) .then() .statusCode(OK); } public void delete(String storageId, String repositoryId, String path) throws ArtifactOperationException { delete(storageId, repositoryId, path, false); } public void delete(String storageId, String repositoryId, String path, boolean force) throws ArtifactOperationException { String url = getContextBaseUrl() + "/storages/" + storageId + "/" + repositoryId + "/" + path; givenLocal().contentType(MediaType.TEXT_PLAIN_VALUE) .param("force", force) .when() .delete(url) .peek() .then() .statusCode(OK); } public ExtractableResponse getResourceWithResponse(String path, String pathVar) { String url = getContextBaseUrl() + "/" + path; if (pathVar != null && !pathVar.isEmpty()) { url += "/" + pathVar; } return givenLocal().contentType(MediaType.TEXT_PLAIN_VALUE) .when() .get(url) .peek() .then() .extract(); } public void rebuildMetadata(String storageId, String repositoryId, String path) throws IOException, JAXBException { String url = getContextBaseUrl() + "/metadata?" + (storageId != null ? "storageId=" + storageId : "") + (repositoryId != null ? (storageId != null ? "&" : "") + "repositoryId=" + repositoryId : "") + (path != null ? (storageId != null || repositoryId != null ? "&" : "") + "path=" + path : ""); givenLocal().contentType(MediaType.TEXT_PLAIN_VALUE) .when() .post(url) .peek() .then() .statusCode(OK); } public void rebuildIndexes(String storageId, String repositoryId, String path) throws IOException { String url = getContextBaseUrl() + "/index?" + (storageId != null ? "storageId=" + storageId : "") + (repositoryId != null ? (storageId != null ? "&" : "") + "repositoryId=" + repositoryId : "") + (path != null ? (storageId != null || repositoryId != null ? "&" : "") + "path=" + path : ""); givenLocal().contentType(MediaType.TEXT_PLAIN_VALUE) .when() .post(url) .peek() .then() .statusCode(OK); } public void removeVersionFromMetadata(String storageId, String repositoryId, String artifactPath, String version, String classifier, String metadataType) { String url = getContextBaseUrl() + "/metadata/" + storageId + "/" + repositoryId + "/" + (artifactPath != null ? artifactPath : ""); givenLocal().contentType(MediaType.TEXT_PLAIN_VALUE) .params("version", version, "classifier", classifier, "metadataType", metadataType) .when() .delete(url) .peek() .then() .statusCode(OK); } public String search(String query, String mediaType, String searchProvider) throws UnsupportedEncodingException { return search(null, query, mediaType, searchProvider); } public String search(String repositoryId, String query, String mediaType, String searchProvider) throws UnsupportedEncodingException { String url = getContextBaseUrl() + "/search"; if (repositoryId == null) { repositoryId = ""; } else { repositoryId = URLEncoder.encode(repositoryId, "UTF-8"); } query = URLEncoder.encode(query, "UTF-8"); return givenLocal().params("repositoryId", repositoryId, "q", query, "searchProvider", searchProvider) .header("accept", mediaType) .when() .get(url) .then() .statusCode(OK) .extract() .response() .getBody() .asString(); } }