com.applitools.eyes.ServerConnector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eyes-connectivity-java4-jersey1x Show documentation
Show all versions of eyes-connectivity-java4-jersey1x Show documentation
Applitools Eyes SDK REST API communication implementation depending on Jersey version 1x
/*
* Applitools SDK for Selenium integration.
*/
package com.applitools.eyes;
import com.applitools.utils.ArgumentGuard;
import com.applitools.utils.GeneralUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.sun.jersey.api.client.AsyncWebResource;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.async.TypeListener;
import org.apache.commons.io.IOUtils;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Future;
/**
* Provides an API for communication with the Applitools agent
*/
public class ServerConnector extends RestClient
implements IServerConnector {
private static final int TIMEOUT = 1000 * 60 * 5; // 5 Minutes
private static final String API_PATH = "/api/sessions/running";
private static final String DEFAULT_CHARSET_NAME = "UTF-8";
private String apiKey = null;
/***
* @param logger A logger instance.
* @param serverUrl The URI of the Eyes server.
*/
public ServerConnector(Logger logger, URI serverUrl) {
super(logger, serverUrl, TIMEOUT);
endPoint = endPoint.path(API_PATH);
}
/***
* @param logger A logger instance.
*/
@SuppressWarnings("WeakerAccess")
public ServerConnector(Logger logger) {
this(logger, GeneralUtils.getDefaultServerUrl());
}
/***
* @param serverUrl The URI of the Eyes server.
*/
public ServerConnector(URI serverUrl) {
this(null, serverUrl);
}
public ServerConnector() {
this((Logger) null);
}
/**
* Sets the API key of your applitools Eyes account.
*
* @param apiKey The api key to set.
*/
public void setApiKey(String apiKey) {
ArgumentGuard.notNull(apiKey, "apiKey");
this.apiKey = apiKey;
}
/**
* @return The currently set API key or {@code null} if no key is set.
*/
public String getApiKey() {
return this.apiKey != null ? this.apiKey : System.getenv("APPLITOOLS_API_KEY");
}
/**
* Sets the proxy settings to be used by the rest client.
*
* @param abstractProxySettings The proxy settings to be used by the rest client.
* If {@code null} then no proxy is set.
*/
@SuppressWarnings("UnusedDeclaration")
public void setProxy(AbstractProxySettings abstractProxySettings) {
setProxyBase(abstractProxySettings);
// After the server is updated we must make sure the endpoint refers
// to the correct path.
endPoint = endPoint.path(API_PATH);
}
/**
* @return The current proxy settings used by the rest client,
* or {@code null} if no proxy is set.
*/
@SuppressWarnings("UnusedDeclaration")
public AbstractProxySettings getProxy() {
return getProxyBase();
}
/**
* Sets the current server URL used by the rest client.
*
* @param serverUrl The URI of the rest server.
*/
@SuppressWarnings("UnusedDeclaration")
public void setServerUrl(URI serverUrl) {
setServerUrlBase(serverUrl);
// After the server is updated we must make sure the endpoint refers
// to the correct path.
endPoint = endPoint.path(API_PATH);
}
/**
* @return The URI of the eyes server.
*/
@SuppressWarnings("UnusedDeclaration")
public URI getServerUrl() {
return getServerUrlBase();
}
/**
* Starts a new running session in the agent. Based on the given parameters,
* this running session will either be linked to an existing session, or to
* a completely new session.
*
* @param sessionStartInfo The start parameters for the session.
* @return RunningSession object which represents the current running
* session
* @throws EyesException For invalid status codes, or if response parsing
* failed.
*/
public RunningSession startSession(SessionStartInfo sessionStartInfo)
throws EyesException {
ArgumentGuard.notNull(sessionStartInfo, "sessionStartInfo");
logger.verbose("Using Jersey1 for REST API calls.");
String postData;
ClientResponse response;
int statusCode;
List validStatusCodes;
boolean isNewSession;
RunningSession runningSession;
try {
// since the web API requires a root property for this message
jsonMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
postData = jsonMapper.writeValueAsString(sessionStartInfo);
// returning the root property addition back to false (default)
jsonMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
} catch (IOException e) {
throw new EyesException("Failed to convert " +
"sessionStartInfo into Json string!", e);
}
try {
response = endPoint.queryParam("apiKey", getApiKey()).
accept(MediaType.APPLICATION_JSON).
entity(postData, MediaType.APPLICATION_JSON_TYPE).
post(ClientResponse.class);
} catch (RuntimeException e) {
logger.log("startSession(): Server request failed: " + e.getMessage());
throw e;
}
// Ok, let's create the running session from the response
validStatusCodes = new ArrayList<>();
validStatusCodes.add(ClientResponse.Status.OK.getStatusCode());
validStatusCodes.add(ClientResponse.Status.CREATED.getStatusCode());
runningSession = parseResponseWithJsonData(response, validStatusCodes,
RunningSession.class);
// If this is a new session, we set this flag.
statusCode = response.getStatus();
isNewSession = (statusCode == ClientResponse.Status.CREATED.getStatusCode());
runningSession.setIsNewSession(isNewSession);
return runningSession;
}
/**
* Stops the running session.
*
* @param runningSession The running session to be stopped.
* @return TestResults object for the stopped running session
* @throws EyesException For invalid status codes, or if response parsing
* failed.
*/
public TestResults stopSession(final RunningSession runningSession,
final boolean isAborted, final boolean save)
throws EyesException {
ArgumentGuard.notNull(runningSession, "runningSession");
final String sessionId = runningSession.getId();
ClientResponse response;
List validStatusCodes;
TestResults result;
HttpMethodCall delete = new HttpMethodCall() {
public ClientResponse call() {
String currentTime = GeneralUtils.toRfc1123(
Calendar.getInstance(TimeZone.getTimeZone("UTC")));
// Building the request
WebResource.Builder builder = endPoint.path(sessionId)
.queryParam("apiKey", getApiKey())
.queryParam("aborted", String.valueOf(isAborted))
.queryParam("updateBaseline", String.valueOf(save))
.accept(MediaType.APPLICATION_JSON)
.header("Eyes-Expect", "202-accepted")
.header("Eyes-Date", currentTime);
// Actually perform the method call and return the result
return builder.delete(ClientResponse.class);
}
};
response = sendLongRequest(delete, "stopSession");
// Ok, let's create the running session from the response
validStatusCodes = new ArrayList<>();
validStatusCodes.add(ClientResponse.Status.OK.getStatusCode());
result = parseResponseWithJsonData(response, validStatusCodes,
TestResults.class);
return result;
}
@Override
public void deleteSession(TestResults testResults) {
ArgumentGuard.notNull(testResults, "testResults");
WebResource sessionsResources = restClient.resource(serverUrl);
WebResource.Builder builder = sessionsResources
.path("/api/sessions/batches/")
.path(testResults.getBatchId())
.path("/")
.path(testResults.getId())
.queryParam("apiKey", getApiKey())
.queryParam("AccessToken", testResults.getSecretToken())
.accept(MediaType.APPLICATION_JSON);
builder.delete();
}
/**
* Matches the current window (held by the WebDriver) to the expected
* window.
*
* @param runningSession The current agent's running session.
* @param matchData Encapsulation of a capture taken from the application.
* @return The results of the window matching.
* @throws EyesException For invalid status codes, or response parsing
* failed.
*/
public MatchResult matchWindow(RunningSession runningSession,
MatchWindowData matchData)
throws EyesException {
ArgumentGuard.notNull(runningSession, "runningSession");
ArgumentGuard.notNull(matchData, "data");
ClientResponse response;
List validStatusCodes;
MatchResult result;
String jsonData;
// since we rather not add an empty "tag" param
WebResource runningSessionsEndpoint =
endPoint.path(runningSession.getId());
// Serializing data into JSON (we'll treat it as binary later).
// IMPORTANT This serializes everything EXCEPT for the screenshot (which
// we'll add later).
try {
jsonData = jsonMapper.writeValueAsString(matchData);
} catch (IOException e) {
throw new EyesException("Failed to serialize data for matchWindow!",
e);
}
// Convert the JSON to binary.
byte[] jsonBytes;
ByteArrayOutputStream jsonToBytesConverter = new ByteArrayOutputStream();
try {
jsonToBytesConverter.write(
jsonData.getBytes(DEFAULT_CHARSET_NAME));
jsonToBytesConverter.flush();
jsonBytes = jsonToBytesConverter.toByteArray();
} catch (IOException e) {
throw new EyesException("Failed create binary data from JSON!", e);
}
// Getting the screenshot's bytes (notice this can be either
// compressed/uncompressed form).
byte[] screenshot = DatatypeConverter.parseBase64Binary(
matchData.getAppOutput().getScreenshot64());
// Ok, let's create the request data
ByteArrayOutputStream requestOutputStream = new ByteArrayOutputStream();
DataOutputStream requestDos = new DataOutputStream(requestOutputStream);
byte[] requestData;
try {
requestDos.writeInt(jsonBytes.length);
requestDos.flush();
requestOutputStream.write(jsonBytes);
requestOutputStream.write(screenshot);
requestOutputStream.flush();
// Ok, get the data bytes
requestData = requestOutputStream.toByteArray();
// Release the streams
requestDos.close();
} catch (IOException e) {
throw new EyesException("Failed send check window request!", e);
}
// Sending the request
response = runningSessionsEndpoint.queryParam("apiKey", getApiKey()).
accept(MediaType.APPLICATION_JSON).
entity(requestData, MediaType.APPLICATION_OCTET_STREAM_TYPE).
post(ClientResponse.class);
// Ok, let's create the running session from the response
validStatusCodes = new ArrayList<>(1);
validStatusCodes.add(ClientResponse.Status.OK.getStatusCode());
result = parseResponseWithJsonData(response, validStatusCodes,
MatchResult.class);
return result;
}
@Override
public void downloadString(URL uri, boolean isSecondRetry, final IDownloadListener listener) {
AsyncWebResource target = Client.create().asyncResource(uri.toString());
AsyncWebResource.Builder request = target.accept(MediaType.WILDCARD);
request.get(new TypeListener(ClientResponse.class) {
public void onComplete(Future f) {
int status = 0;
ClientResponse clientResponse = null;
try {
clientResponse = f.get();
status = clientResponse.getStatus();
if (status > 300) {
logger.verbose("Got response status code - " + status);
listener.onDownloadFailed();
return;
}
InputStream entityInputStream = clientResponse.getEntityInputStream();
StringWriter writer = new StringWriter();
IOUtils.copy(entityInputStream, writer, "UTF-8");
String theString = writer.toString();
listener.onDownloadComplete(theString);
} catch (Exception e) {
GeneralUtils.logExceptionStackTrace(e);
logger.verbose("Failed to parse request(status= " + status + ") = "+ clientResponse.getEntity(String.class));
listener.onDownloadFailed();
}
}
});
}
@Override
public String postDomSnapshot (String domJson){
WebResource target = restClient.resource(serverUrl).path(("api/sessions/running/data")).queryParam("apiKey", getApiKey());
byte[] resultStream = GeneralUtils.getGzipByteArrayOutputStream(domJson);
WebResource.Builder request = target.accept(MediaType.APPLICATION_JSON).entity(resultStream, MediaType.APPLICATION_OCTET_STREAM_TYPE);
ClientResponse response = request.post(ClientResponse.class);
MultivaluedMap headers = response.getHeaders();
List location = headers.get("Location");
String entity = null;
if (!location.isEmpty()) {
entity = location.get(0);
}
return entity;
}
@Override
public String postViewportImage(String base64Image) {
WebResource target = restClient.resource(serverUrl).path(("api/sessions/running/data")).queryParam("apiKey", getApiKey());
byte[] screenshot = DatatypeConverter.parseBase64Binary(base64Image);
ByteArrayOutputStream requestOutputStream = new ByteArrayOutputStream();
DataOutputStream requestDos = new DataOutputStream(requestOutputStream);
byte[] requestData;
String result = null;
try {
requestOutputStream.write(screenshot);
requestOutputStream.flush();
requestData = requestOutputStream.toByteArray();
requestDos.close();
WebResource.Builder request = target.accept(MediaType.APPLICATION_JSON).entity(requestData, MediaType.APPLICATION_OCTET_STREAM_TYPE);
ClientResponse response = request.post(ClientResponse.class);
MultivaluedMap headers = response.getHeaders();
List location = headers.get("Location");
if (!location.isEmpty()) {
result = location.get(0);
}
} catch (IOException e) {
logger.verbose("Failed to send viewport image");
}
return result;
}
@Override
public Map> postLocators(VisualLocatorsData visualLocatorsData) {
WebResource target = restClient.resource(serverUrl).path("api/locators/locate").queryParam("apiKey", getApiKey());
String postData;
try {
jsonMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
postData = jsonMapper.writeValueAsString(visualLocatorsData);
} catch (IOException e) {
throw new EyesException("Failed to convert " +
"visualLocatorsData into Json string!", e);
}
WebResource.Builder request = target.accept(MediaType.APPLICATION_JSON).entity(postData, MediaType.APPLICATION_JSON_TYPE);
ClientResponse response = request.post(ClientResponse.class);
List validStatusCodes = new ArrayList<>();
validStatusCodes.add(Response.Status.OK.getStatusCode());
validStatusCodes.add(Response.Status.CREATED.getStatusCode());
return parseResponseWithJsonData(response, validStatusCodes, new TypeReference
© 2015 - 2025 Weber Informatics LLC | Privacy Policy