API Samples Overview

The Issuetrak API is a RESTful API that uses the HTTP protocol as the primary transport infrastructure both to invoke an API operation and to transmit the response from an API operation back to the API consumer. As such, the API is platform-agnostic.

Any API consumer platform that can transmit the appropriately defined payload via a valid HTTP application protocol request using the API HMAC authorization scheme (see the API Authorization Overview article for details about the authorization scheme) can communicate with the API.

Issuetrak has created sample applications to demonstrate how to communicate with the API using Microsoft .NET, PHP, Python, and NodeJS. Please reference the samples from the aforementioned zip file using your favorite text editor, with the exception of Python, which we provide as code examples within this article.

You can get the samples from the zip file attached here.


 

.NET Sample

The .NET API sample relies upon an Issuetrak.API.Client library distributed with the sample as a .NET assembly. This Issuetrak.API.Client includes a set of wrapper methods to encapsulate the details of the process of calling the Issuetrak API for a particular operation.

For example, to retrieve a Note for a specific Note ID value, the Issuetrak.API.Client library exposes the following method signature:

Task<IssuetrakAPIResponse<ReadNoteDTO>> GetNoteForNoteIDAsync(GetNoteForNoteIDRequest request)

This signature indicates that the wrapper method is an asynchronous method that will return a Task with a task result of type <IssuetrakAPIResponse<ReadNoteDTO>>. The IssuetrakAPIResponse class provides a wrapper to encapsulate the details of an API response with properties such as “ResponseStatusCode” to retrieve the HTTP status code of the HTTP response and “ResponseText” to provide the raw string response of the HTTP response.

To call the GetNoteForNoteIDAsync API method, the Issuetrak.API.Client library may be invoked as in the following C# example:

using (IssuetrakAPIClient client = new IssuetrakAPIClient(API_KEY)) 
{ 
  GetNoteForNoteIDRequest request = 
     new GetNoteForNoteIDRequest(BASE_API_URL, API_VERSION, noteID);
  IssuetrakAPIResponse response = await client.GetNoteForNoteIDAsync(request);
}

In the sample, the “API_KEY” constant is the API key text value that is used for the API authorization scheme. The “BASE_API_URL” constant is the root URL to the Issuetrak API instance, and the API_VERSION constant is the integer identifier for the desired API version, e.g., 1.

The “noteID” parameter is a variable that is used to identify the desired Note ID value to retrieve.


 

PHP Sample

The PHP sample project demonstrates how to interact with the Issuetrak API via standard PHP functions to include code samples to:

  1. Create a New Note
  2. Retrieve a Note by Note ID
  3. Retrieve Issues by Issue Number List

The PHP sample also demonstrates how to employ standard PHP functions to generate the authorization headers for API authorization.

To execute the PHP sample, copy the text for the “index.php” file to an appropriately configured PHP web server and update the “$base_url” and “$api_key” PHP variables with the appropriate values for the Issuetrak API deployment.


 

Python Samples

The Python sample projects demonstrate how to interact with the Issuetrak API via Python to:

  1. Retrieve an Issue
  2. Update an Issue
  3. Submit an Issue


from uuid import uuid4
from hashlib import sha512
from datetime import datetime
from urlparse import urlparse
import hmac
import json
import base64
import httplib

SITE_URL = "http://subdomain.myissuetraksite.com/nestedsite1/nestedsite2/api/v1"
API_KEY = "dUd4ZzQ3dDNvM1lrYzFoY1lHYmh3anRjQkJHb1dBWWdWTHdOd25kZm9GQQ="

class ApiAuthorization:
    __apiKey = ""

    def __init__(self, apiKey):
        self.__apiKey = apiKey

    def __generateMessageToHash(self, httpVerb, requestId, requestTimeStamp, absoluteUrlPath, requestQuery = "", requestBody = ""):
        return "\n".join([httpVerb, requestId, requestTimeStamp, absoluteUrlPath, requestQuery, requestBody])

    def __computeHash(self, apiKey, messageToBeHashed):
        hashText = None
        hashKeyBytes = str(apiKey).encode('UTF-8')
        messageToBeHashedBytes = str(messageToBeHashed).encode('UTF-8')

        hmacHash = hmac.new(hashKeyBytes, messageToBeHashedBytes, sha512).digest()
        hashText = base64.b64encode(hmacHash).decode('UTF-8')
        
        return hashText

    @staticmethod
    def __getUtcDate():
        return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f0Z")

    def __getConnection(self, protocol, url):
        return httplib.HTTPConnection(url, 80) if (protocol == "http") else httplib.HTTPSConnection(url, 443)

    def __generateHeaders(self, requestId, requestTimestamp, hashedMessage):
        return {
            "X-IssueTrak-API-Request-ID": requestId,
            "X-IssueTrak-API-Timestamp": requestTimestamp,
            "X-IssueTrak-API-Authorization": hashedMessage,
            "content-type": "application/json"
        }

    def performApiCall(self, httpVerb, endpointUrl, requestQuery = "", requestBody = ""):
        urlParts = urlparse(endpointUrl)
        baseUrl = urlParts.netloc.lower()
        absolutePath = urlparse(endpointUrl).path.lower()
        requestId = str(uuid4()).lower()
        requestTimestamp = self.__getUtcDate()
        messageToHash = self.__generateMessageToHash(httpVerb, requestId, requestTimestamp, absolutePath, requestQuery, requestBody)
        hashedMessage = self.__computeHash(self.__apiKey, messageToHash)
        
        connection = self.__getConnection(urlParts.scheme, baseUrl)
        headers = self.__generateHeaders(requestId, requestTimestamp, hashedMessage)

        connection.request(httpVerb, endpointUrl, requestBody, headers)
        return connection.getresponse()

def performGet(siteUrl, apiKey, endpointUrl):
    url = "{0}{1}".format(siteUrl, endpointUrl)
    apiAuthorization = ApiAuthorization(apiKey)
    return apiAuthorization.performApiCall("GET", url)

def performPost(siteUrl, apiKey, endpointUrl, requestQuery = '', requestBody = ''):
    url = "{0}{1}".format(siteUrl, endpointUrl)
    apiAuthorization = ApiAuthorization(apiKey)
    return apiAuthorization.performApiCall("POST", url, requestQuery, requestBody)

def performPut(siteUrl, apiKey, endpointUrl, requestQuery = '', requestBody = ''):
    url = "{0}{1}".format(siteUrl, endpointUrl)
    apiAuthorization = ApiAuthorization(apiKey)
    return apiAuthorization.performApiCall("PUT", url, requestQuery, requestBody)

#######################################
# GET
# Retrieves issue #1 from issuetrak
#######################################
print("------Issue Retrieve------")
response = performGet(SITE_URL, API_KEY, "/issues/false/1")
getResponseData = response.read().decode()
print(getResponseData)

#######################################
# PUT
# Updates issue #1 in issuetrak
#######################################
updateData = json.loads(getResponseData)

# Remove fields not on the issue update DTO
del updateData["Metadata"]
del updateData["ExtensionData"]
del updateData["Notes"]
del updateData["SubmittedDate"]
del updateData["SLAComplianceStatus"]
del updateData["Slaid"]
for userDefinedField in updateData["UserDefinedFields"]:
    del userDefinedField["ExtensionData"]
    del userDefinedField["Metadata"]
    del userDefinedField["DisplayName"]

# Remove fields that are disabled or inactive
del updateData["CauseID"]
del updateData["DepartmentID"]
del updateData["IssueSubType4ID"]

# Make changes to existing fields with updated data
updateData["Subject"] = "Altering my subject"

# Perform request
print("\n\n------Issue Update------")
response = performPut(SITE_URL, API_KEY, "/issues", "", json.dumps(updateData))
print(response.read().decode())

#######################################
# POST
# Insert issue into Issuetrak
#######################################

# Set disabled/inactive fields to None
requestBody = {
  "ShouldSuppressEmailForCreateOperation": True,
  "Notes": [
    {
      "CreatedDate": "{0}".format(datetime.now()),
      "CreatedBy": "admin",
      "NoteText": "This is a note",
      "IsPrivate": False,
      "IsRichText": True
    }
  ],
  "UserDefinedFields": [
    {
      "UserDefinedFieldID": 1,
      "Value": "This is text that will go into one of my user defined fields"
    },
    {
      "UserDefinedFieldID": 1008,
      "Value": "{0}".format(datetime.now().strftime('%Y-%m-%dT%H:%M:%S'))
    }
  ],
  "SubmittedDate": "{0}".format(datetime.now()),
  "EnteredBy": "admin",
  "SeverityID": None,
  "Subject": "This is a test subject submitted via the Issuetrak API in Python",
  "Description": "This is the description of the test issue",
  "IsDescriptionRichText": True,
  "IssueTypeID": 1,
  "IssueSubTypeID": 0,
  "IssueSubType2ID": 0,
  "IssueSubType3ID": 0,
  "IssueSubType4ID": None,
  "PriorityID": 1,
  "AssetNumber": 0,
  "LocationID": "",
  "SubmittedBy": "admin",
  "AssignedTo": None,
  "TargetDate": None,
  "RequiredByDate": None,
  "NextActionTo": None,
  "SubStatusID": 0,
  "ProjectID": 0,
  "OrganizationID": 1,
  "ShouldNeverSendEmailForIssue": False,
  "ClassID": 1,
  "DepartmentID": None,
  "SpecialFunction1": "string",
  "SpecialFunction2": "string",
  "SpecialFunction3": "string",
  "SpecialFunction4": "string",
  "SpecialFunction5": "string"
}

print("\n\n------Insert Issue------")
response = performPost(SITE_URL, API_KEY, "/issues", "", json.dumps(requestBody))
print(response.read().decode())


from uuid import uuid4
from urllib.parse import urlparse
from hashlib import sha512
from datetime import datetime
import hmac
import json
import base64
import http.client

SITE_URL = "http://subdomain.myissuetraksite.com/nestedsite1/nestedsite2/api/v1"
API_KEY = "dUd4ZzQ3dDNvM1lrYzFoY1lHYmh3anRjQkJHb1dBWWdWTHdOd25kZm9GQQ="

class ApiAuthorization:
    __apiKey = ""
    __siteUrl = ""

    def __init__(self, apiKey, siteUrl):
        self.__apiKey = apiKey
        self.__siteUrl = siteUrl

    def __generateMessageToHash(self, httpVerb, requestId, requestTimeStamp, absoluteUrlPath, requestQuery = "", requestBody = ""):
        return "\n".join([httpVerb, requestId, requestTimeStamp, absoluteUrlPath, requestQuery, requestBody])

    def __computeHash(self, apiKey, messageToBeHashed):
        hashText = None
        hashKeyBytes = bytes(apiKey, 'UTF-8')
        messageToBeHashedBytes = bytes(messageToBeHashed, 'UTF-8')

        hmacHash = hmac.new(hashKeyBytes, messageToBeHashedBytes, sha512).digest()
        hashText = base64.b64encode(hmacHash).decode('UTF-8')
        
        return hashText

    @staticmethod
    def __getUtcDate():
        return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f0Z")

    def __getConnection(self, protocol, url):
        return http.client.HTTPConnection(url, 80) if (protocol == "http") else http.client.HTTPSConnection(url, 443)

    def __generateHeaders(self, requestId, requestTimestamp, hashedMessage):
        return {
            "X-IssueTrak-API-Request-ID": requestId,
            "X-IssueTrak-API-Timestamp": requestTimestamp,
            "X-IssueTrak-API-Authorization": hashedMessage,
            "content-type": "application/json"
        }

    def __performApiCall(self, httpVerb, endpointUrl, requestQuery = "", requestBody = ""):
        fullUrl = f"{self.__siteUrl}{endpointUrl}"
        urlParts = urlparse(fullUrl)
        baseUrl = urlParts.netloc.lower()
        absolutePath = urlparse(fullUrl).path.lower()
        requestId = str(uuid4()).lower()
        requestTimestamp = self.__getUtcDate()
        messageToHash = self.__generateMessageToHash(httpVerb, requestId, requestTimestamp, absolutePath, requestQuery, requestBody)
        hashedMessage = self.__computeHash(self.__apiKey, messageToHash)
        
        connection = self.__getConnection(urlParts.scheme, baseUrl)
        headers = self.__generateHeaders(requestId, requestTimestamp, hashedMessage)

        connection.request(httpVerb, fullUrl, requestBody, headers)
        return connection.getresponse()

    def performGet(self, endpointUrl):
        return self.__performApiCall("GET", endpointUrl)

    def performPost(self, endpointUrl, requestQuery = '', requestBody = ''):
        return self.__performApiCall("POST", endpointUrl, requestQuery, requestBody)

    def performPut(self, endpointUrl, requestQuery = '', requestBody = ''):
        return self.__performApiCall("PUT", endpointUrl, requestQuery, requestBody)

#######################################
# Initialize Api Authorization Class
#######################################
apiAuthorization = ApiAuthorization(API_KEY, SITE_URL)

#######################################
# GET
# Retrieves issue #1 from issuetrak
#######################################
print("------Issue Retrieve------")
response = apiAuthorization.performGet("/issues/false/1")
getResponseData = response.read().decode()
print(getResponseData)

#######################################
# PUT
# Updates issue #1 in issuetrak
#######################################
updateData = json.loads(getResponseData)

# Remove fields not on the issue update DTO
del updateData["Metadata"]
del updateData["ExtensionData"]
del updateData["Notes"]
del updateData["SubmittedDate"]
del updateData["SLAComplianceStatus"]
del updateData["Slaid"]
for userDefinedField in updateData["UserDefinedFields"]:
    del userDefinedField["ExtensionData"]
    del userDefinedField["Metadata"]
    del userDefinedField["DisplayName"]

# Remove fields that are disabled or inactive
del updateData["CauseID"]
del updateData["DepartmentID"]
del updateData["IssueSubType4ID"]

# Make changes to existing fields with updated data
updateData["Subject"] = "Altering my subject"

# Perform request
print("\n\n------Issue Update------")
response = apiAuthorization.performPut("/issues", "", json.dumps(updateData))
print(response.read().decode())

#Update a UDF value, if its ID matches the provided field ID, without wiping other UDFs
for userDefinedField in updateData["UserDefinedFields"]:
        if userDefinedField["UserDefinedFieldID"] == 4:
            userDefinedField["Value"] = "new value"
            
#Set a value for a specific UDF ID on a specific issue number
add_memo = {"IssueNumber":"12257 "UserDefinedFieldID":4, "Value":"1234"}
    if re.search('Document Number', getResponseData) == None:
        updateData["UserDefinedFields"].append(add_memo)

#######################################
# POST
# Insert issue into Issuetrak
#######################################

# Set disabled/inactive fields to None
requestBody = {
  "ShouldSuppressEmailForCreateOperation": True,
  "Notes": [
    {
      "CreatedDate": f"{datetime.now()}",
      "CreatedBy": "admin",
      "NoteText": "This is a note",
      "IsPrivate": False,
      "IsRichText": True
    }
  ],
  "UserDefinedFields": [
    {
      "UserDefinedFieldID": 1,
      "Value": "This is text that will go into one of my user defined fields"
    },
    {
      "UserDefinedFieldID": 1008,
      "Value": f"{datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}"
    }
  ],
  "SubmittedDate": f"{datetime.now()}",
  "EnteredBy": "admin",
  "SeverityID": None,
  "Subject": "This is a test subject submitted via the Issuetrak API in Python",
  "Description": "This is the description of the test issue",
  "IsDescriptionRichText": True,
  "IssueTypeID": 1,
  "IssueSubTypeID": 0,
  "IssueSubType2ID": 0,
  "IssueSubType3ID": 0,
  "IssueSubType4ID": None,
  "PriorityID": 1,
  "AssetNumber": 0,
  "LocationID": "",
  "SubmittedBy": "admin",
  "AssignedTo": None,
  "TargetDate": None,
  "RequiredByDate": None,
  "NextActionTo": None,
  "SubStatusID": 0,
  "ProjectID": 0,
  "OrganizationID": 1,
  "ShouldNeverSendEmailForIssue": False,
  "ClassID": 1,
  "DepartmentID": None,
  "SpecialFunction1": "string",
  "SpecialFunction2": "string",
  "SpecialFunction3": "string",
  "SpecialFunction4": "string",
  "SpecialFunction5": "string"
}

print("\n\n------Insert Issue------")
response = apiAuthorization.performPost("/issues", "", json.dumps(requestBody))
print(response.read().decode())


NodeJS Samples

The NodeJS sample project demonstrates how to insert a note into an issue by calling the notes API end point.


Java Sample

The Java sample project facilitates barebones API authorization so that additional code (provided by your developer) can communicate with your site's API.


Conclusion

These samples are to provide a general overview of how a request can be built. A single API application can incorperate multiple requests to GET data, and based on the response either PUT in an update to the record or POST a new request / record.

Should there be any problems with executing an API call to your Issuetrak site, please reach out to our Support team and we will be happy to help review the code for the selected request.