Page tree
Skip to end of metadata
Go to start of metadata


The SeeTest Reporter provides you with a Rest API that gives you direct access to the test data and statistics.

Concepts

  1. Key - Key and value construct a tag. Each test has a subset of available tags with various values that describe the test specifics. Example: date.
  2. Project - Concept derived from the cloud. Each project has it's own resources (users, devices) and can be used to created separation of testing efforts.
  3. Test - Single test execution instance.

Endpoints

Following are the available APIs:


URL

Verb

Returns

/api/keys

GET

All available keys of the currently selected project, response:


[
    {
        "id": number,
        "name": string,
        "index": number,
        "dataType": string,
        "valueCount": 0,
        "lastUsage": number, // Unix time
        "protected": boolean
    },
	...
]
/api/keysPOST

Create a new key, payload:

{ "name": string, "type": string }

Currently only "STRING" type is allowed.

Response is the full new key entity created.

/api/keys/{id}DELETEDeletes the key identified by {id}

/api/tests/{id}

GET

Test with a specified id of the currently selected project.

/api/tests/{id}/project/{projectName}/GETTest with specified id of the specified selected project. Note the "/" at the end.

/api/tests/list

/api/tests/list?projectId={projectId}

/api/tests/list?projectName={projectName}



POST

List the tests of the currently selected project given TestsRequest (filter, sort, limit, offset, keys to include) object sent.

TestsRequest object properties:

{

"returnTotalCount": boolean // optional, return total record count, default false

"sort": [ { "property": "fieldName or key", "descending": false }, ... ], // optional

"filter": [ { "property": "fieldName or key", "operator": "=", "value": "string value" }, ... ] // optional

"limit": number, // optional, used as page size

"page": number, // optional

"searchValue": "string value", // optional, applied to all selected key's columns with like predicate

"keys": [ "key1", "key2", ...] // optional, keys to be included in the response else only test's fixed columns returned

}

"filter" items are connected with AND operator.

item's operator supports: "=", "<>", ">", ">=", "<", "<=", "contains", "startswith", "endswith".

Comparisons use string semantics.

DataResponse:

{

count: number, // if returnTotalCount == true

data: [ {...}, {...}, ... ]

}

projectId / projectName request parameters:

You can optionally request other current selected project's tests sending Reporter's project id or project's name.

/api/tests/grouped

/api/tests/grouped?projectId={projectId}

/api/tests/grouped?projectName={projectName}


POST

Returns grouped counts from tests given TestsRequest (filter, sort, keys to group by, pivot by for counts: such success and/or status_code) object sent.

TestRequest object is the same as in list but:

  • "keys" property are the keys used to "group by".
  • Additional optional property: "pivotBy" which is an string array which can contains "success" and/or "status". "success" add two columns to each result row: "successCount" and "unsuccessCount". "status" adds tree columns: "passedCount", "failedCount", "incompleteCount".
  • All resulting rows includes the column "count" along "pivotBy" columns if any.

projectId / projectName request parameters:

You can optionally request other current selected project's groped data sending Reporter's project id or project's name.

/api/tests/distinct

/api/tests/distinct?projectId={projectId}

/api/tests/distinct?projectName={projectName}


POST

Returns distinct key values from tests given TestsRequest (filter, sort) object sent.

TestRequest object is the same as in /list but:

  • "keys" property are the keys used in "select distinct".

projectId / projectName request parameters:

You can optionally request other current selected project's groped data sending Reporter's project id or project's name.

/api/tests/csv?request={TestRequest}

/api/tests/csv?request={TestRequest}

&projectId={projectId}

/api/tests/csv?request={TestRequest}

&projectName={projectName}

GET

Download tests in CSV format given TestsRequest (filter, sort, limit, offset, keys to include), you can find TestsRequest's description in /list end point.

projectId / projectName request parameters:

You can optionally request other current selected project's tests sending Reporter's project id or project's name.

/api/tests/delete

/api/tests/delete?projectId={projectId}

/api/tests/delete?projectName={projectName}

POST

As the name suggest, deletes the desired tests, payload is an array of ID of the tests to be delete, for example:

[ 5, 67, 100 ]

projectId / projectName request parameters:

You can optionally request delete test from other project than current selected sending Reporter's project id or project's name.

/api/projectsGETList all projects.
/api/projects/{oldName}/{newName}/PUT

Change project name from oldName to newName. Note the "/" at the end.

/api/{projectId}/{testId}/attachmentsGET

Reporter With Cloud

Download all the attachments of projectId / testId as zip file.

Note that projectId is Reporter's project ID not Cloud's

The file name of the exported attachments is test_id_{testId}_attachments.zip

/api/{projectName}/{testId}/attachments-nameGET

Reporter With Cloud

Download all the attachments of projectName / testId as zip file.

The file name of the exported attachments is test_id_{testId}_attachments.zip

/api/{testId}/attachmentsGET

Reporter Without Cloud

Download all the attachments of testId as zip file.

The file name of the exported attachments is test_id_{testId}_attachments.zip

/api/supportData


Gets support data (config, logs and data), flows:

Flow #1:

The client waits until the file is ready then grabs it.

/options (optional) > /prepare > /file

Flow #2:

The client polls from time to time until file is ready then grabs it.

/options (optional) > /start > /status (repeat periodically until state <> RUNNING) > /file

/api/supportData/optionsGET

Returns what can be included in support data file, properties are:

configReporter's configuration files (config/ folder).
logsReporter's log files(logs/ folder).
databaseReporter's database's data, true when pg_dump utility is available.
databaseConfig

PG's configuration file, true when:

  • PG is running in the same host as Reporter.
  • Reporter's database user has permission to query 'data_directory' setting.
  • Reporter's OS user has permissions to access PG's 'data_directory'.
databaseLogPG's log files (latest <=10 MB), same rules as above.
/api/supportData/preparePOST

Prepares the support file replying until is ready.

Payload:

{

"config": <boolean>, // Include config/ folder contents?

"logs": <boolean>, // Include logs/ folder contents?

"database": <boolean>, // Include database's data?

"databaseConfig": <boolean>, // Include PG's configuration file?

"databaseLog": <boolean> // Include PG's log files?

}

Note that the process may take several minutes depending on the selected options and the size of them.

To get the prepared file you have to hit /api/supportData/file.

/api/supportData/startPOSTStart the support file preparation in background, payload is the same as above.
/api/supportData/statusGET

Return the current status of the support file preparation process:

{

"state": <string>, // One of this: IDLE, RUNNING, COMPLETED, CANCELING, CANCELED, ERROR

"startedAt": <long>, // Timestamp in which the process stared

"startedBy": <string>, // Username who started the process.

"canceledBy": <string>, // Username who canceled the process (status CANCELING or CANCELED)

"progress": <number>, // Percentage completed.

"finishedAt": <long>, // Timestamp in which the process finished or canceled.

"error": <string> // In case the process ended because unexpected error, here is the error message.

}

Note that "progress" may be slightly more than 100%; this is because at the beginning of the process the total data size is computed but it increases while process runs.

/api/supportData/fileGETGets the support data file.
/api/testViewGET

List all test views of the current selected project.

Test View's returned properties:

  • id
  • name
  • byKey
  • createdBy
  • showInDashboard
/api/testView/listPOST

List test views given DataRequest object received as body. This is meant to be used mainly by UI because retrieves data by page and allows you to sort and filter by test view's name.

DataRequest properties:

{

"limit": number, // required, used as page size

"page": number, // required, starting at 1

"sort": [ { "property": "fieldName", "descending": false }, ... ], // optional

"searchValue": string // optional, returns test views which name contains this value, case insensitive

}

DataResponse:

{

count: number // total count of records or count of records if "searchValue" was sent.

data: [ {...}, {...}, ... ] // requested page records

}

Test View's returned properties:

  • id
  • name
  • byKey
  • createdBy
  • showInDashboard
/api/testView/{id}GET

Retrieves the test view by ID. This service returns the full test view entity which has the following structure:

{
"id" : number,
"name" : string,
"byKey" : string, // required, key name selected in "View by" panel
"byKeyValue" : string, // required, value selected in "View by" panel
"byKeyDesc" : boolean, // desired order of list of valued of "View by" panel
"groupByKey1" : string, // required, key name selected in "Group by" left panel
"groupByKey2" : string, // required, key name selected in "Group by" right panel
"filter" : [ {
"id" : number,
"testViewId" : number,
"property" : string // required, field or key name to be compared against value
"operator" : string, // required, comparison operator to be applied
"value" : string, // required, value to which field or key will be compared
"isByKey" : boolean, // flag indicating if the filter item is related to "View by" panel
"isStatus" : boolean // flag indicating if the filter item is related to status filter
} ... ],
"keys" : [ "key name", ... ], // selected key names to be shown in tests list

"createdBy" : string,

"showInDashboard" : boolean // is the test view shown in dashboard?
}

/api/testView/{id}/summary

/api/testView/{id}/summary?filter=<JSON Object>

GET

Retrieves tests counts applying filter defined in the received test view's id, optionally receiving additional filter as JSON object, examples:

GET /api/testView/23/summary

Returns the tests counts of test view id 23.

GET /api/testView/23/summary?filter={"buildId":"4"}

Returns the tests counts of test view id 23 applying additional filter item "buildId" ="4"

Response example:

{
  "data": [
    {
      "passedCount": 33,
      "failedCount": 17,
      "incompleteCount": 14,
      "skippedCount": 0,
      "_count_": 64
    }
  ]
}

HTTP Status other than 200 (OK) denotes and error such:

StatusProblemResponse
400At least one of the parameters are incorrect
{
  "timestamp": 1564506251329,
  "status": 400,
  "error": "Bad Request",
  "exception": "java.lang.IllegalArgumentException",
  "message": "Invalid filter property: fiscal",
  "path": "/api/testView/50/summary"
}
404Requested data not found
{
  "timestamp": 1564506398043,
  "status": 404,
  "error": "Not Found",
  "exception": "javax.persistence.EntityNotFoundException",
  "message": "TestView id:51 does not exits.",
  "path": "/api/testView/51/summary"
}
403Permissions problem, for example user does not have permission to access the test view's project
{
  "timestamp": 1564518253500,
  "status": 403,
  "error": "Forbidden",
  "exception": "org.springframework.security.access.AccessDeniedException",
  "message": "You have no permission to access the desired project.",
  "path": "/api/testView/200/summary"
}
500Other internal problems
{
  "timestamp": 1564506426191,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "com.fasterxml.jackson.databind.JsonMappingException",
  "message": "No content to map due to end-of-input\n at [Source: ; line: 1, column: 0]",
  "path": "/api/testView/50/summary"
}
/api/testViewPOST

Creates or updates a full test view. The structure of the object send as body is the same as above.

To create a new test view omit "id" and "createdBy" properties in the root element, "id" and "testViewId" in the "filter" items.

To update an existing test view, retrieve it first, make the appropriate changes then send back.

Here is a an example request.

Test View POST request
POST /api/testView
{
    "name": "new2",
    "byKey": "date",
    "groupByKey1": "date",
    "keys": [
        "not.exists"
    ]
}
/api/testViewPUT

Updates selected properties of the test view. The expected body for this service is:

{
"id" : number, // required
"name" : string, // optional
"showInDashboard" : boolean // optional
}

Include only those the properties you want to update.

/api/testView/{id}DELETEDeletes the test view identified by the received ID.
  • All actions are available for all users role.

Authentication

If the SeeTest Reporter server is configured to be secured, you will need to authenticate all the requests to the server.

We support 2 authentication methods:

Basic Authentication

Add the authentication headers as follows:

Authentication: Basic < Base64 encoded <username>:<password> >
ReporterProject: <project name>


Access Key

Generate access from SeeTest Cloud.

This key identifies the user and its project.

For additional information about generating access keys in the SeeTest Cloud, visit this link.


Add the token to your request as follows:

Authentication: Bearer <access key>

The examples below use the Unirest HTTP library.  To compile and run them, use the following Maven dependency:


Maven dependency
<dependency>
    <groupId>com.mashape.unirest</groupId>
    <artifactId>unirest-java</artifactId>
    <version>1.4.9</version>
</dependency>


Code Example

Java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.junit.Test;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

public class APIConfluence {
    private HttpResponse<String> responseString;
    private HttpResponse<InputStream> responseInputStream;

    private String urlBase = "http://hostname:port/";   //TODO: modify hostname and port of your Reporter

    private String user = "user";  //TODO: user name
    private String password = ".....";  //TODO: user password
    String projectName = "projectName";//TODO: project name is here
    String projectID = "projectID";//TODO: project ID is here

    private String testID = "test ID";//TODO: test ID is here

    @Test
    public void getKeys() {
        String url = urlBase + "/api/keys";

        try {
            responseString = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asString();
            System.out.println(responseString.getBody());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void getSpecifiedTest() {
        String url = urlBase + "/api/tests/" + testID;
        try {
            responseString = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asString();
            System.out.println(responseString.getBody());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void getTestsByProjectName() {

        String url = urlBase + "/api/tests/" + testID + "/project/" + projectName + "/";


        try {
            responseString = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asString();

            System.out.println(responseString.getBody());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void TestsList() {
        String url = urlBase + "/api/tests/list";
        HttpResponse<JsonNode> response = null;
        try {
            response = Unirest.post(url)
                    .basicAuth(user, password)
                    .header("Content-Type", "application/json")
                    .body("{\"returnTotalCount\":true, \"limit\":10, \"page\":1, \"sort\":[{\"property\":\"test_id\",\"descending\":false}],\r\n" +
                            "   \"keys\":[\"date\",\"device.os\",\"device.version\"],\r\n" +
                            "   \"filter\":[{\"property\":\"device.os\",\"operator\":\"=\",\"value\":\"iOS\"}]}")//TODO: insert key and value
                    .asJson();
        } catch (UnirestException e) {
            e.printStackTrace();
        }
        if (Objects.nonNull(response)) {
            System.out.println(response.getBody());
        }
    }



    @Test
    public void getGroupedCounts() {
        String url = urlBase + "/api/tests/grouped";

        try {
            responseString = Unirest.post(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .body("{\"returnTotalCount\":true, \"limit\":10, \"page\":1, \"sort\":[{\"property\":\"test_id\",\"descending\":false}],\r\n" +
                            "   \"keys\":[\"date\",\"device.os\",\"device.version\"],\r\n" +
                            "   \"filter\":[{\"property\":\"device.os\",\"operator\":\"=\",\"value\":\"iOS\"}]}")//TODO: insert key and value
                    .asString();

            System.out.println(responseString.getBody());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Test
    public void getDistinctKey() {
        String url = urlBase + "api/tests/distinct";

        try {
            responseString = Unirest.post(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .body("{\"returnTotalCount\":true, \"limit\":10, \"page\":1, \"sort\":[{\"property\":\"test_id\",\"descending\":false}],\r\n" +
                            "   \"keys\":[\"date\",\"device.os\",\"device.version\"],\r\n" +
                            "   \"filter\":[{\"property\":\"device.os\",\"operator\":\"=\",\"value\":\"iOS\"}]}")//TODO: insert key and value
                    .asString();

            System.out.println(responseString.getBody());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Test
    public void getAllProjects() {
        String url = urlBase + "/api/projects";

        try {
            responseString = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asString();

            System.out.println(responseString.getBody());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void changeProjectName() {
        String oldName = "oldName";//TODO project old name
        String newName = "newName";//TODO project new name
        String url = urlBase + "/api/projects/" + oldName + "/" + newName + "/";
        try {
            responseString= Unirest.put(url)
                    .basicAuth(user, password)
                    .header("Content-Type", "application/json")
                    .asString();
            System.out.println(responseString.getBody());
        } catch (UnirestException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void getAttachmentsByProjectID() {

        try {
            String url = urlBase + "/api/" + projectID + "/" + testID + "/attachments";

            responseInputStream = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asBinary();

            String destination = "destination";//TODO: Path to save the file - zip file
            writeToFile(destination);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void getAttachmentsByProjectName() {
        String url = urlBase + "/api/" + projectName + "/" + testID + "/attachments-name";


        try {
            responseInputStream = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asBinary();

            String destination = "destination";//TODO: Path to save the file - zip file
            writeToFile(destination);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void getAttachmentsByTestID() {
        String url = urlBase + "/api/" + testID + "/attachments";


        try {
            responseInputStream = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asBinary();

            String destination = "destination";//TODO: Path to save the file - zip file
            writeToFile(destination);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Test
    public void getSupportDataOptions() {
        String url = urlBase + "/api/supportData/options";

        try {
            responseString = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asString();
            System.out.println(responseString.getBody());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void prepareSupportData() {
        String url = urlBase + "/api/supportData/prepare";

        try {
            responseString = Unirest.post(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .body("{\n" +
                            "  \"config\" : true,\n" +
                            "  \"logs\" : true,\n" +
                            "  \"database\" : true,\n" +
                            "  \"databaseConfig\" : true,\n" +
                            "  \"databaseLog\" : true\n" +
                            "}")
                    .asString();
            System.out.println(responseString.getBody());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void getSupportDataFile() {
        String url = urlBase + "/api/supportData/file";

        try {
            responseInputStream = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asBinary();

            String destination = "destination";//TODO: Path to save the file - zip file
            writeToFile(destination);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void startSupportData() {
        String url = urlBase + "/api/supportData/start";

        try {
            responseString = Unirest.post(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .body("{\n" +
                            "  \"config\" : true,\n" +
                            "  \"logs\" : true,\n" +
                            "  \"database\" : true,\n" +
                            "  \"databaseConfig\" : true,\n" +
                            "  \"databaseLog\" : true\n" +
                            "}")
                    .asString();
            System.out.println(responseString.getBody());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void getSupportDataStatus() {
        String url = urlBase + "/api/supportData/status";

        try {
            responseString = Unirest.get(url)
                    .basicAuth(user, password)
                    .header("content-type", "application/json")
                    .asString();
            System.out.println(responseString.getBody());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void writeToFile(String destination) {
        InputStream inputStream = responseInputStream.getRawBody();
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(destination);

            int bytesRead = -1;
            byte[] buffer = new byte[4096];
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, bytesRead);
            }
            fileOutputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}