Skip to content

REST Interface

You can use REST protocol to interact with a TACTIC server. Please have a look at the REST API Quick Start if you haven't done so.

For the TACTIC portal, the endpoints have the base url of the form:

https://portal.southpawtech.com/<site>/<project>/REST/<method>

Where "method" is the particular API method to be called.

Most of the API can be accessed with POST requests. This allows for the flexibility of arguments being passed onto the methods that are not limited to a single line on the URL. For the purposes of this tutorial, a sample project has been set up on the TACTIC Portal so that you can test the API.

https://portal.southpawtech.com/trial/api_test/REST/<method>

In Python:

The following code snippet uses the Python requests module to make a rest call to TACTIC Portal to retrieve a list of jobs.

site = "trial"
project = "api_test"
ticket = 'tactic123'

endpoint = "https://portal.southpawtech.com/%s/%s/REST/query" % (site, project)

search_type = "workflow/job"
data = {
    'search_type': search_type,
}

headers = {
    'Authorization': "Bearer " + ticket,
    'Accept': 'application/json',
}


r = requests.post(endpoint, headers=headers, data=data)
sobjects = r.json()

for sobject in sobjects:
    print("item: ", sobject.get("code"), sobject.get("name") )

In Javascript

The same format can be used in Javascript using the "fetch" method.

let site = "trial";
let project = "api_test";
let ticket = 'tactic123';

let endpoint = "https://portal.southpawtech.com/"+site+"/"+project+"/REST/query";

//query for a list of jobs
let search_type = "workflow/job"
let data = {
    search_type: search_type,
};

let headers = {
    Authorization: "Bearer " + ticket,
    Accept: 'application/json',
}

let r = await fetch( endpoint, {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(data),
} )
let sobjects = await r.json()

sobjects.foreach( (sobject) => {
    console.log("item: ", sobject.code, sobject.name);
} )

Wrap in a function

You may notice that many of the functions have the same form, so it may be useful to wrap this into a central function in your codebase:

const call_tactic = (method, kwargs) => {

    let data = kwargs

    let portal_url = "https://portal.southpawtech.com"
    let site = "trial";
    let project = "api_test";
    let ticket = 'tactic123';

    let base_endpoint = portal_url + "/" + site + "/" + project + "/REST";
    let endpoint = base_endpoint + "/" + method;

    let headers = {
        Authorization: "Bearer " + ticket,
        Accept: 'application/json',
    }

    let response = await fetch( endpoint, {
        method: 'POST',
        mode: 'cors',
        headers: headers,
        body: JSON.stringify(data),
    } )

    if (response.status == 200) {
        let ret = await response.json()
        return  ret;
    }
    else {
        throw("Error calling TACTIC Server");
    }
}

Then you can simply call the query above with the following:

let jobs = call_tactic( "query", { search_type: "workflow/jobs" } )

Examples

With this method of accessing TACTIC, you can just pass in different "data" variables to use any of TACTIC's API methods.

Some more examples:

Generate a new access ticket.

Often you need to have a user log into the system with their username and password. The TACTIC API can be used to authenticate the user and return a ticket which will subsequently be used in future calls to the API.

let base_endpoint = "https://portal.southpawtech.com/"+site+"/"+project+"/REST";
let endpoint = base_endpoint + "/get_ticket";

let data = {
    login: 'trial_user',
    password: 'tactic123';
}

let r = await fetch( enpoint, {
    method: 'POST',
    body: JSON.stringify(data),
} )

let ticket = r.json();

Once you have a ticket, it is useful to store it somewhere other than memory so that it can be reused (local storage or a cookie, for example). Tickets have an expiry date so users will be prompted to log in again upon expiry.

Query with filters

let base_endpoint = "https://portal.southpawtech.com/"+site+"/"+project+"/REST";
let endpoint = base_endpoint + "/query";

let search_type = "workflow/job"
let data = {
    search_type: search_type,
    filters: [['status', 'Pending']],
}

let headers = {
    Authorization: "Bearer " + ticket,
    Accept: 'application/json',
}


let r = await fetch( enpoint, {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(data),
} )

let jobs = await r.json();

Use the expression language

This will get all of the assets sObjects in a job.

let base_endpoint = "https://portal.southpawtech.com/"+site+"/"+project+"/REST";
let endpoint = base_endpoint + "/eval";

let data = {
    expression: "@SOBJECT(workflow/job['status', 'Pending'].workflow/job_asset)"
}

let headers = {
    Authorization: "Bearer " + ticket,
    Accept: 'application/json',
}

let r = await fetch( url, {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(data),
} )

let assets = await r.json();