Table of contents
Overview
The Testify API allows you to send sensor data to Testify so that it can be used as part of a checklist. It is designed for companies trying to automate some of the checks in their Testify checklists.
Modes of operation
There are 2 ways to send data to Testify:
Registered Devices can send data to Testify at any time and it will be saved by Testify in case it is needed by a checklist. Any mechanism to send HTTP requests can be used here.
Public Devices need to be publicly accessible, so that Testify can send a request to it in case its value is needed by a checklist. Any HTTP server with JSON responses can be used here.
Available check types
Regardless if you use Registered Devices or Public Devices, you will need to know what check type you are sending or providing to Testify:
Check Type | Supported format |
---|---|
TextCheck | string in UTF-8 format |
NumericCheck | number (including floating point numbers with 12 places before and after the decimal point) |
LogicCheck | boolean |
SingleChoiceCheck | GUID of single choice option in Testify (string) |
MultipleChoiceCheck | GUIDs of multiple choice options in Testify (list of strings) |
TestobjectSelection | GUIDs of test objects in Testify (string) |
TimeCheck | time in ISO8601 format (string) |
DateCheck | date in ISO8601 format (string) |
FileCheck | base64-encoded file (string) + mimeType (string) or data URL (string), this can either be a list of files or a single file |
PhotoCheck | base64-encoded image (string) + mimeType (string) or data URL (string), this can only be a single photo |
Notice that for FileChecks and PhotoChecks, Testify needs to be provided with the MIME type of the photo or the file that was sent to them. This is necessary so that Testify can display the file or photo properly.
Allowed MIME types for FileChecks
File type | MIME type |
---|---|
| |
.txt |
|
.xml |
|
.zip |
|
.docx |
|
.xlsx |
|
.doc |
|
.xls |
|
Allowed MIME types for PhotoChecks
Photo type | MIME type |
---|---|
.png |
|
.bmp |
|
.gif |
|
.jpg |
|
.jpeg |
|
.tiff |
|
.svg |
|
Further reading
If you are interested in Registered Devices, look into Registered Devices for general information or Registered Device Examples for real world examples with code in some popular programming languages.
If you are interested in Public Devices, look into Public Devices for general information or Public Device Examples for real world examples with code in some popular programming languages.
Public Devices
Public Devices need to be publicly accessible, so that Testify can send a request to it in case its value is needed by a checklist.
Technology
Any HTTP server with JSON responses can be used here. This means that any public API like weather APIs and others can act as Public Devices.
The structure of this JSON response can be very flexible, however it must be possible to decide which part of the JSON response (e.g. which property) should be used as a check result within Testify by specifying a JSON Path (see chapter "JSON paths").
Important: Make sure that this API is reachable on the Internet (e.g. have a public IP address or domain). Otherwise Testify will not be able to access it. The easiest way to test this is to try accessing this API with a different internet access than the one of your company (e.g. your private mobile phone).
Samples in some commonly used programming languages and frameworks can be found in Public Device Examples.
Connecting the API to Testify
Wheather you created your own API or use an existing one, you have to connect it to Testify . This can be done in the Testify web interface in Devices
. If you create a Public Device there, you will need to provide:
URL: url of the API
HTTP method: what HTTP method should Testify use to make the request to the API. This is
GET
in most cases.HTTP headers: any HTTP headers that Testify should send along with the request. This might be needed for an API-key.
Check type: what type of data you want to make accessible to Testify. For a list of available check types visit Overview.
For any check other than FileCheck
s or PhotoCheck
s :
JSON path: used to locate the exact value you want to provide to Testify. For more information see "Using JSON paths" below.
If you selected FileCheck
or PhotoCheck
, you have to use one of the following options instead of the JSON path mentioned above:
MIME type: enter the MIME type of the file or photo you want to send manually
JSON path to value: JSON path to the value you want to provide to Testify
OR
JSON path to MIME type: JSON path to the MIME type
JSON path to value: JSON path to the value you want to provide to Testify
OR (for
FileChecks
only)JSON path to file array: JSON path to the array of FileChecks. This array needs to have the following structure:
[ { "value": YOUR_BASE64_STRING, "mimeType": YOUR_MIME_TYPE }, // other array elements here ]
Using JSON paths
JSON paths are used to extract a specific value from some JSON.
When accessing information from a public device, this JSON path tells Testify what information in the HTTP response it should use.
For example, if we have the following HTTP response:
{ "firstName": "Max", "lastName" : "Mustermann", "age" : 26, "address" : { "streetAddress": "Peter-Behrens-Platz 10", "city" : "Linz", "postalCode" : "4020" }, "phoneNumbers": [ { "type" : "iPhone", "number": "0123456789" }, { "type" : "Home", "number": "0112233" } ] }
And we want to get the value iPhone
we can use the following JSON path:
$.phoneNumbers[0].type
In the simplest case you might have a response like this:
{ "temperature": 3.14159 }
And we would get the value 3.14159
like this:
$.temperature
For more information on this concept visit https://jsonpath.com/ .
Public Device Examples
The purpose of this example is to setup a HTTP server that returns "Hello world"
. This server is then connected to Testify.
C#
using System.Text.Json; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => { return JsonSerializer.Serialize(new { text = "Hello world" }); }); app.Run();
Note:
This example is done using a C# Minimal API for simplicity, but similar functionality can also be achieved when using a more traditional ASP.NET API with controllers.
Python
from flask import Flask app = Flask(__name__) @app.route("/") def get_data(): return { "text": "Hello world" } if __name__ == "__main__": app.run()
Note:
This example uses
flask
which is a lightweight web framework. It can be installed withpip install Flask
.The last
if
is unnecessary if the python script is run with the commandflask run
. However, this takes more steps to setup which is why it hasn't been done here. For more information visit https://www.twilio.com/blog/how-run-flask-application . The script above works by simply runningpy FILENAME.py
as you would expect.
Javascript
import express from "express"; const app = express(); app.get("/", (req, res) => { res.json({ "text": "Hello world" }); }) app.listen(3000, () => { console.log("Listening on port 3000"); });
Note:
This example uses express, which can be installed with
npm install express
.
Connecting the API to Testify
In order to make the "Hello World"
-API that we created above accessible to Testify, we need to create a Public Device in the Testify web interface with the following options:
URL: depends on where you run your server
HTTP method:
GET
HTTP headers: None
Check type: TextCheck
JSON path:
$.text
Other check types
Depending on the check type, you need to send to provide data in a different format.
See Overview for a list of all check types and the format they require.
See other check types in Registered Device Examples for examples in C#, Python and Javascript.
Registered Devices
Registered Devices can send data to Testify at any time and it will be saved by Testify in case it is needed by a checklist.
Technology
The API is a public HTTP endpoint that accepts JSON data. Therefore any programming or scripting language capable of sending HTTP requests can be used to send data to the API.
Samples in some commonly used programming languages can be found in Registered Device Examples.
Access
The API is accessible at the following URL: https://externaldatastore.azurewebsites.net/api/device/CHECK_TYPE
where CHECK_TYPE
is the CheckType you want to send data for.
Authentication
You must include a deviceId
with every API request. This deviceId
can be created with the Testify web interface in Devices
.
In Testify this can be created under Administration -> Equipment Types -> Equipment in the section "External Devices".
Request structure
There are 3 ways to add the deviceId
to your HTTP request. Therefore, there are 3 different ways your HTTP request might look.
deviceId in Query-String
Add ?deviceId=YOUR_DEVICE_ID
to the end of your request url.
POST https://externaldatastore.azurewebsites.net/api/device/CHECK_TYPE?deviceId=YOUR_DEVICE_ID HTTP/1.1 Content-Type: application/json { "value": YOUR_VALUE }
deviceId as a HTTP-Header
Add a deviceId
HTTP-Header with the value of YOUR_DEVICE_ID
.
POST https://externaldatastore.azurewebsites.net/api/device/CHECK_TYPE HTTP/1.1 Content-Type: application/json deviceId: YOUR_DEVICE_ID { "value": YOUR_VALUE }
deviceId in the HTTP-Body
Add a deviceId
property to the JSON in the HTTP body.
POST https://externaldatastore.azurewebsites.net/api/device/CHECK_TYPE HTTP/1.1 Content-Type: application/json { "value": YOUR_VALUE, "deviceId": YOUR_DEVICE_ID }
Note:
value
is the actual value that you want to send to the API. This varies depending on the check type.
Custom timestamp
By default Testify uses the timestamp of the HTTP request as a creationDate
. However, if the time you get data from your sensor and the time you send it to the API vary, you might consider adding a creationDate
manually.
POST https://externaldatastore.azurewebsites.net/api/device/CHECK_TYPE?deviceId=YOUR_DEVICE_ID HTTP/1.1 Content-Type: application/json { "value": YOUR_VALUE, "creationDate": YOUR_CUSTOM_CREATION_DATE }
Adding MIME types to the Request
For FileChecks and PhotoChecks you need to add the MIME type to the request. There are 2 ways to do that:
With an additional property
The easiest way is to add the property valueMimeType
to the http request and specify the appropriate MIME type there.
POST https://externaldatastore.azurewebsites.net/api/device/CHECK_TYPE?deviceId=YOUR_DEVICE_ID HTTP/1.1 Content-Type: application/json { "value": { "value": YOUR_BASE64_STRING, "mimeType": YOUR_MIME_TYPE } }
With a data URL
Another way to add the MIME type to the request is to use a data URL instead of standard base64-string.
Data urls are essentially base64-strings with MIME-types attached to them. For example:
data:YOUR_MIME_TYPE;base64,YOUR_BASE64_STRING
So with this data URL, your request could look like this:
POST https://externaldatastore.azurewebsites.net/api/device/CHECK_TYPE?deviceId=YOUR_DEVICE_ID HTTP/1.1 Content-Type: application/json { "value": { "value": YOUR_DATA_URL } }
Note:
CHECK_TYPE
is eitherFileCheck
orPhotoCheck
YOUR_BASE64_STRING
is the base64-representation of the file or image you want to sendYOUR_MIME_TYPE
is one of the allowed MIME types listed in Overview
Response status codes
After sending data to the Testify API, you can look at the HTTP status code of the response to determine wheater your request was successful or failed. If a request failed, status codes can provide additional information on why it failed.
HTTP Status Code | Explanation |
---|---|
201 - Created | Everything worked as expected. |
400 - Bad Request | Some required parameters are not provided. |
401 - Unauthorized | The provided |
422 - Unprocessable Entity | The shape of the JSON body isn't right or the mime type doesn't match the file or photo that was sent |
Registered Device Examples
This simple example sends the string "Hello world"
to the Testify API as a TextCheck
.
C#
HttpClient client = new HttpClient(); string url = "https://externaldatastore.azurewebsites.net/api/device/TextCheck?deviceId=YOUR_DEVICE_ID"; object payload = new { value = "Hello world" }; HttpContent httpContent = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"); var result = await client.PostAsync(url, httpContent); Console.WriteLine(result.StatusCode);
Note:
The
HttpClient
should only be instantiated once (don't create a new one if you have to make multiple requests).client.PostAsync
is an asynchronous method which allows other code to run while we wait for the response. However, here we want to work with the result immidiately after the POST request, which is whyawait
is used to force the program to wait for the response. In other scenarios (e.g. when having multiple API calls), removingawait
can lead to better performance.
Python
import requests url = "https://externaldatastore.azurewebsites.net/api/device/TextCheck?deviceId=YOUR_DEVICE_ID" payload = { "value": "Hello world" } response = requests.post(url = url, json = payload, headers={"Content-Type": "application/json"}) print(response.status_code)
Note:
The library
requests
has to be installed for this to work. It can be installed usingpip install requests
.
Javascript
let url = "https://externaldatastore.azurewebsites.net/api/device/TextCheck?deviceId=YOUR_DEVICE_ID"; let payload = { "value": "Hello world" }; fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }).then((response) => { console.log(response.status); })
Note:
When Javascript is run in a browser
fetch
is available by default. However, if we want to run it in Node.js we have to install it first by runningnpm install node-fetch
and importing it at the top of our file withimport fetch from "node-fetch"
.fetch
returns a Promise, which can be handled withthen
like in the example above or withasync/await
. For more information visit https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/async_function .
Curl
curl -X POST -i -H "Content-Type: application/json" -d "{\"value\": \"Hello world\"}" https://externaldatastore.azurewebsites.net/api/device/TextCheck?deviceId=YOUR_DEVICE_ID
Note for all of the examples above:
YOUR_DEVICE_ID
needs to be replaced with your specificdeviceId
. Information on how to get this can be found at [Authentication in Registered Devices](Registered Devices.md).
Other authentication methods
If you don't want to add your deviceId
as a query parameter at the end of the url like in the example above, you can also use one of the following two approaches:
deviceId as a HTTP-Header
deviceId
is added as a HTTP header with the value of YOUR_DEVICE_ID
.
HttpContent httpContent = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"); httpContent.Headers.Add("deviceId", YOUR_DEVICE_ID);
response = requests.post(url = url, json = payload, headers={ "Content-Type": "application/json", "deviceId": YOUR_DEVICE_ID })
fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "deviceId": YOUR_DEVICE_ID }, body: JSON.stringify(payload) }).then((response) => { console.log(response.status); });
deviceId in the HTTP-Body
deviceId
is added in the payload of the HTTP request.
object payload = new { value = YOUR_VALUE, deviceId = YOUR_DEVICE_ID });
payload = { "value": YOUR_VALUE, "deviceId": YOUR_DEVICE_ID }
let payload = { "value": YOUR_VALUE, "deviceId": YOUR_DEVICE_ID };
Other check types
Depending on the check type, the value you send to the API has to be of a certain type (see Available check types in Overview). Here are some examples using all the different check types in different programming languages.
TextCheck
string text = "Hello";
text = "Hello"
let text = "Hello";
NumericCheck
double number = 3.14159;
number = 3.14159
let number = 3.14159;
LogicCheck
bool logic = false;
logic = False
let logic = false;
SingleChoiceCheck
Here you need to provide the GUID of the choice from the single choice check that we want to select.
string singleChoice = "e0607e71-56c6-4a62-9112-ce0eab4cdcf5";
single_choice = "e0607e71-56c6-4a62-9112-ce0eab4cdcf5"
let singleChoice = "e0607e71-56c6-4a62-9112-ce0eab4cdcf5";
MultipleChoiceCheck
Here you need to provide the GUID of the choices from the multiple choice check that we want to select.
string[] multipleChoices = new string[] { "e0607e71-56c6-4a62-9112-ce0eab4cdcf5", "e0607e71-56c6-4a62-9112-ce0eab4cdcf3" };
multiple_choices = ["e0607e71-56c6-4a62-9112-ce0eab4cdcf5", "e0607e71-56c6-4a62-9112-ce0eab4cdcf3"]
let multipleChoices = ["e0607e71-56c6-4a62-9112-ce0eab4cdcf5", "e0607e71-56c6-4a62-9112-ce0eab4cdcf3"];
TestobjectSelection
Here you need to provide the GUID of the test objects you want to select.
string[] testObject = new string[] { "e0607e71-56c6-4a62-9112-ce0eab4cdcf5", "e0607e71-56c6-4a62-9112-ce0eab4cdc32" };
test_object = ["e0607e71-56c6-4a62-9112-ce0eab4cdcf5", "e0607e71-56c6-4a62-9112-ce0eab4cdc32"]
let testObject = ["e0607e71-56c6-4a62-9112-ce0eab4cdcf5", "e0607e71-56c6-4a62-9112-ce0eab4cdc32"];
TimeCheck
If you use language that doesn't allow you to create a Timeonly datatype (Javascript) or where the serializer doesn't support the Timeonly type yet (C#), you have 2 options:
Create a normal Date (Javascript) or DateTime (C#) object and enter random values for the date (the date will be ignored by the Testify API).
Send a string representation of the time you want.
DateTime time = new DateTime(1970, 1, 1, 20, 15, 33, 0); string timeAsString = "20:15:33";
time = datetime.time(20, 15, 33, 0).__str__()
let time = new Date(1970, 1, 1, 20, 15, 33, 0); let timeAsString = "20:15:33";
DateCheck
DateTime date = new DateTime(2021, 12, 24);
date = datetime.date(2021, 12, 24).__str__()
let date = new Date(2021, 12, 24);
FileCheck
FileChecks are somewhat different from other checks:
You can send multiple files with one request (notice that a list of objects is used).
You have to provide a mime type for every file. For a list of all allowed mime types visit Allowed MIME types for FileChecks.
string fileDataBase64 = Convert.ToBase64String(File.ReadAllBytes("./assets/file.zip")); object[] fileDataList = new[] { new { value = fileDataBase64, mimeType = "application/zip" } };
file_data = open("./assets/file.zip", "rb").read() file_data_base64 = base64.b64encode(file_data).decode("utf-8") # .decode("utf-8") so that it's a string file_data_list = [ { "value": file_data_base64, "mimeType": "application/zip" } ]
let fileDataBase64 = await fs.readFile("./assets/file.zip", { encoding: 'base64' }); let fileDataList = [ { "value": fileDataBase64, "mimeType": "application/zip" } ];
PhotoCheck
Photo are somewhat different from other checks, as you have to provide a mime type for every photo you send to the API. For a list of all allowed mime types visit Allowed MIME types for PhotoChecks.
string photoDataBase64 = Convert.ToBase64String(File.ReadAllBytes("./assets/photo.png")); object photoDataObject = new { value = photoDataBase64, mimeType = "image/png" };
photo_data = open("./assets/photo.png", "rb").read() photo_data_base64 = base64.b64encode(photo_data).decode("utf-8") # .decode("utf-8") so that it's a string photo_data_object = { "value": photo_data_base64, "mimeType": "image/png" }
let photoDataBase64 = await fs.readFile("./assets/photo.png", { encoding: 'base64' }); let photoDataObject = { "value": photoDataBase64, "mimeType": "image/png" };
Notice that we don't use a list here like we do with FileChecks.
For the functionality in Testify, key users can find more information at Equipment or Messmittel.