Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 148 additions & 6 deletions src/main/java/com/saucelabs/saucerest/api/InsightsEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@

import com.saucelabs.saucerest.DataCenter;
import com.saucelabs.saucerest.HttpMethod;
import com.saucelabs.saucerest.JobSource;
import com.saucelabs.saucerest.Unfinished;
import com.saucelabs.saucerest.model.insights.AnalyticsTrendsParameter;
import com.saucelabs.saucerest.model.insights.Errors;
import com.saucelabs.saucerest.model.insights.ErrorsParameter;
import com.saucelabs.saucerest.model.insights.TestCases;
import com.saucelabs.saucerest.model.insights.TestCasesParameter;
import com.saucelabs.saucerest.model.insights.TestCasesStats;
import com.saucelabs.saucerest.model.insights.TestCasesStatsParameter;
import com.saucelabs.saucerest.model.insights.TestMetrics;
import com.saucelabs.saucerest.model.insights.TestMetricsParameter;
import com.saucelabs.saucerest.model.insights.TestResult;
import com.saucelabs.saucerest.model.insights.TestResultParameter;
import com.saucelabs.saucerest.model.insights.Tests;
import com.saucelabs.saucerest.model.insights.TestsParameter;
import com.saucelabs.saucerest.model.insights.TestTrends;
import com.saucelabs.saucerest.model.insights.TrendsErrors;
import com.saucelabs.saucerest.model.insights.TrendsErrorsParameter;
import com.saucelabs.saucerest.model.insights.TrendsTestsParameter;
import java.io.IOException;
import java.util.Map;

@Unfinished("This endpoint is not yet completely implemented")
@Unfinished("This endpoint does not yet cover the Filters, Activity, Concurrency, and Coverage Insights APIs, "
+ "nor the cross-source (all sources) variants of the Test Cases, Errors, and Trends APIs")
public class InsightsEndpoint extends AbstractEndpoint {
public InsightsEndpoint(DataCenter dataCenter) {
super(dataCenter);
Expand All @@ -26,11 +42,137 @@ public InsightsEndpoint(String username, String accessKey, String apiServer) {
super(username, accessKey, apiServer);
}

/**
* Retrieves run data for all tests matching the specified criteria.
*
* @param parameter A {@link TestResultParameter} object containing the parameters to filter the results
* @return A {@link TestResult} object
* @throws IOException when the request fails
*/
public TestResult getTestResults(TestResultParameter parameter) throws IOException {
String url = getBaseEndpoint() + "v1/analytics/tests";
Map<String, Object> params;
params = parameter.toMap();

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, params), TestResult.class);
return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestResult.class);
}
}

/**
* Returns aggregated metric values, such as fastest/slowest run and status breakdown, for a specific test
* across a given period.
*
* @param parameter A {@link TestMetricsParameter} object containing the parameters to filter the results
* @return A {@link TestMetrics} object
* @throws IOException when the request fails
*/
public TestMetrics getTestMetricsSummary(TestMetricsParameter parameter) throws IOException {
String url = getBaseEndpoint() + "v1/analytics/insights/test-metrics";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestMetrics.class);
}

/**
* Returns time-bucketed data representing the tests executed in the requested interval.
*
* @param parameter A {@link AnalyticsTrendsParameter} object containing the parameters to filter the results
* @return A {@link TestTrends} object
* @throws IOException when the request fails
*/
public TestTrends getTestTrends(AnalyticsTrendsParameter parameter) throws IOException {
String url = getBaseEndpoint() + "v1/analytics/trends/tests";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestTrends.class);
}

/**
* Returns an array containing details of individual test executions matching the specified criteria.
*
* @param jobSource The type of device for which you are getting tests. Valid values are: {@link JobSource}
* @param parameter A {@link TestsParameter} object containing the parameters to filter the results
* @return A {@link Tests} object
* @throws IOException when the request fails
*/
public Tests getTests(JobSource jobSource, TestsParameter parameter) throws IOException {
String url = getBaseEndpoint(jobSource) + "tests";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), Tests.class);
}

/**
* Returns an array of test cases, grouped by name, with statistical details such as run counts and pass/fail
* rates.
*
* @param jobSource The type of device for which you are getting test cases. Valid values are: {@link JobSource}
* @param parameter A {@link TestCasesParameter} object containing the parameters to filter the results
* @return A {@link TestCases} object
* @throws IOException when the request fails
*/
public TestCases getTestCases(JobSource jobSource, TestCasesParameter parameter) throws IOException {
String url = getBaseEndpoint(jobSource) + "test-cases";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestCases.class);
}

/**
* Returns a high-level statistical summary of test case consistency and reliability, such as counts of
* consistently passing, failing, or erroring test cases.
*
* @param jobSource The type of device for which you are getting test case stats. Valid values are: {@link
* JobSource}
* @param parameter A {@link TestCasesStatsParameter} object containing the parameters to filter the results
* @return A {@link TestCasesStats} object
* @throws IOException when the request fails
*/
public TestCasesStats getTestCasesStats(JobSource jobSource, TestCasesStatsParameter parameter) throws IOException {
String url = getBaseEndpoint(jobSource) + "test-cases/stats";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestCasesStats.class);
}

/**
* Returns an array of errors, with occurrence counts, for all tests matching the specified criteria.
*
* @param jobSource The type of device for which you are getting errors. Valid values are: {@link JobSource}
* @param parameter A {@link ErrorsParameter} object containing the parameters to filter the results
* @return A {@link Errors} object
* @throws IOException when the request fails
*/
public Errors getErrors(JobSource jobSource, ErrorsParameter parameter) throws IOException {
String url = getBaseEndpoint(jobSource) + "errors";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), Errors.class);
}

/**
* Returns an array of buckets with aggregations, such as the number of tests run per browser, device, OS, or
* framework, for the requested interval.
*
* @param jobSource The type of device for which you are getting trends. Valid values are: {@link JobSource}
* @param parameter A {@link TrendsTestsParameter} object containing the parameters to filter the results
* @return A {@link TestTrends} object
* @throws IOException when the request fails
*/
public TestTrends getTrendsTests(JobSource jobSource, TrendsTestsParameter parameter) throws IOException {
String url = getBaseEndpoint(jobSource) + "trends/tests";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestTrends.class);
}

/**
* Returns error statistics, including the tests affected by each error, for the requested interval.
*
* @param jobSource The type of device for which you are getting error trends. Valid values are: {@link
* JobSource}
* @param parameter A {@link TrendsErrorsParameter} object containing the parameters to filter the results
* @return A {@link TrendsErrors} object
* @throws IOException when the request fails
*/
public TrendsErrors getTrendsErrors(JobSource jobSource, TrendsErrorsParameter parameter) throws IOException {
String url = getBaseEndpoint(jobSource) + "trends/errors";

return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TrendsErrors.class);
}

/** The base endpoint of the v2 Insights endpoint APIs. */
protected String getBaseEndpoint(JobSource jobSource) {
return super.getBaseEndpoint() + "v2/insights/" + jobSource.value + "/";
}
}
44 changes: 44 additions & 0 deletions src/main/java/com/saucelabs/saucerest/model/insights/Aggs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.saucelabs.saucerest.model.insights;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class Aggs {
public List<NameCount> browser;

@SerializedName("browserError")
public List<NameCount> browserError;

@SerializedName("browserFail")
public List<NameCount> browserFail;

public List<NameCount> device;

@SerializedName("deviceError")
public List<NameCount> deviceError;

@SerializedName("deviceFail")
public List<NameCount> deviceFail;

@SerializedName("errorMessage")
public List<NameCount> errorMessage;

public List<NameCount> framework;

@SerializedName("frameworkError")
public List<NameCount> frameworkError;

@SerializedName("frameworkFail")
public List<NameCount> frameworkFail;

public List<NameCount> os;

@SerializedName("osError")
public List<NameCount> osError;

@SerializedName("osFail")
public List<NameCount> osFail;

public List<NameCount> owner;
public List<NameCount> status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package com.saucelabs.saucerest.model.insights;

import java.time.LocalDateTime;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

/** Query parameters for {@code GET /v1/analytics/trends/tests}. */
public class AnalyticsTrendsParameter {
private final String start;
private final String end;
private final TimeRange timeRange;
private final Scope scope;
private final Interval interval;
private final String[] browser;
private final String[] build;
private final String[] device;
private final String[] os;
private final TestResultParameter.Status status;
private final String[] tag;
private final TagFilterMode tagFilterMode;

private AnalyticsTrendsParameter(Builder builder) {
start = builder.start;
end = builder.end;
timeRange = builder.timeRange;
scope = builder.scope;
interval = builder.interval;
browser = builder.browser;
build = builder.build;
device = builder.device;
os = builder.os;
status = builder.status;
tag = builder.tag;
tagFilterMode = builder.tagFilterMode;
}

public Map<String, Object> toMap() {
Map<String, Object> parameters = new HashMap<>();

Stream.of(
new AbstractMap.SimpleEntry<>("start", start),
new AbstractMap.SimpleEntry<>("end", end),
new AbstractMap.SimpleEntry<>("time_range", timeRange == null ? null : timeRange.toString()),
new AbstractMap.SimpleEntry<>("scope", scope == null ? null : scope.value),
new AbstractMap.SimpleEntry<>("interval", interval == null ? null : interval.value),
new AbstractMap.SimpleEntry<>("browser", browser),
new AbstractMap.SimpleEntry<>("build", build),
new AbstractMap.SimpleEntry<>("device", device),
new AbstractMap.SimpleEntry<>("os", os),
new AbstractMap.SimpleEntry<>("status", status == null ? null : status.getValue()),
new AbstractMap.SimpleEntry<>("tag", tag),
new AbstractMap.SimpleEntry<>("tag_filter_mode", tagFilterMode == null ? null : tagFilterMode.value)
)
.filter(e -> e.getValue() != null)
.forEach(e -> parameters.put(e.getKey(), e.getValue()));

return parameters;
}

public static final class Builder {
private String start;
private String end;
private TimeRange timeRange;
private Scope scope;
private Interval interval;
private String[] browser;
private String[] build;
private String[] device;
private String[] os;
private TestResultParameter.Status status;
private String[] tag;
private TagFilterMode tagFilterMode;

public Builder setStart(LocalDateTime val) {
start = DateTimeUtils.toUtcString(val);
return this;
}

public Builder setEnd(LocalDateTime val) {
end = DateTimeUtils.toUtcString(val);
return this;
}

public Builder setTimeRange(TimeRange val) {
timeRange = val;
return this;
}

public Builder setScope(Scope val) {
scope = val;
return this;
}

public Builder setInterval(Interval val) {
interval = val;
return this;
}

public Builder setBrowser(String[] val) {
browser = val;
return this;
}

public Builder setBuild(String[] val) {
build = val;
return this;
}

public Builder setDevice(String[] val) {
device = val;
return this;
}

public Builder setOs(String[] val) {
os = val;
return this;
}

public Builder setStatus(TestResultParameter.Status val) {
status = val;
return this;
}

public Builder setTag(String[] val) {
tag = val;
return this;
}

public Builder setTagFilterMode(TagFilterMode val) {
tagFilterMode = val;
return this;
}

public AnalyticsTrendsParameter build() {
boolean isTimeRangeUsed = timeRange != null;
boolean isStartEndUsed = start != null && end != null;

if (!isTimeRangeUsed && !isStartEndUsed) {
throw new IllegalStateException("Either 'time_range' or 'start' and 'end' must be set.");
}
if (isTimeRangeUsed && isStartEndUsed) {
throw new IllegalStateException("Only one of 'time_range' or 'start' and 'end' can be set, not both.");
}

return new AnalyticsTrendsParameter(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.saucelabs.saucerest.model.insights;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

final class DateTimeUtils {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");

private DateTimeUtils() {
}

/** Converts a {@link LocalDateTime} in the system default time zone to a UTC API date string. */
static String toUtcString(LocalDateTime val) {
ZoneOffset offset = ZoneId.systemDefault().getRules().getOffset(val);
LocalDateTime utcDateTime = val.minusSeconds(offset.getTotalSeconds());

return utcDateTime.format(FORMATTER);
}
}
Loading
Loading