#!/usr/bin/env python # coding=utf-8 # Created by: Brent Goodman # Date: April 07, 2018 # Comments: Uses the Python Requests library to do REST API calls to v9 ARSystem Enviornments # Alot of the information is hard-coded, but easy to see how to accomodate other values # etc. All the functions have been tested and work in my new enviornment. All the code # makes use of the "AP-Sample:Restaurant" form for testing. The 'params=' has some # catches that need to be watched out for. I have documented comments through out. # Very limited error checking has been implemented in this version of the code. # Note that C# is my goto language. I only use python for development work on # micro-controllers, so the code I provide may not be the most clean/effecive way # of doing things, but it does get the results needed. import json import requests import datetime import time requests.packages.urllib3.disable_warnings() # Method: GET, POST, PUT, DELET # URL: http://<>/api # Authentication: Basic HTTP, OAuth, none # Custom Headers: Content-Type: application/json # Request Body: # Response Status Codes: 200 OK, 201 Created, 500 Internal Error # Way to find fields that can contain data # select * from field where schemaid = (select schemaid from arschema where name ='Your Form Name') and foption <=3 order by fieldid; # Setup the server name to be used by all the URL's bg_ServerName = "smartitm01-prod.wienkav.at/smartit/app" bg_UserID = "zim9003" bg_Password = "3S1punk08_111$" def doLogin(): myToken = "" bgReturnVal = -1 url = 'https://' + bg_ServerName + '/api/jwt/login' payload = {'username': bg_UserID, 'password': bg_Password} headers = {'content-type': 'application/x-www-form-urlencoded'} r = requests.post(url, data=payload, headers=headers, verify=False) if r.status_code == 200: myToken = r.text bgReturnVal = 1 else: print("Failure...") print("Status Code: " + str(r.status_code)) bgReturnVal = -1 myToken = r.text return(bgReturnVal, myToken) def doLogout(bg_Token): bgTokenType = 'AR-JWT' myURL = 'https://' + bg_ServerName + '/api/jwt/logout' myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token} myR = requests.post(myURL, headers=myHeaders, verify=False) print("") print("Logged out with a status of: {}".format(myR.status_code)) # Pull all data back from form def doPullData(bg_FormName, bg_Token): bgTokenType = 'AR-JWT' myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token} # Pull back all data in the form myR = requests.get(url = myURL, headers = myHeaders, verify=False) print("URL Used:") print(myR.url) print("") if(myR.status_code != 200): print('Status code:', myR.status_code) print('Headers:', myR.headers) print('Error Response:', myR.json()) return(-1) bgData = myR.json() # bgData contains a dictionay of two items: # entries # _links # Copy the dictionary 'entries' into variable avar = bgData['entries'] # len(avar) will tell us the number of returned records # Loop the number of records returned for x in range(len(avar)): # Grab the dictionary of 'values' myVals = avar[x]['values'] # Print out the key/value pair for keys,values in myVals.items(): print('{} => {}'.format(keys,values)) print("") return(len(avar)) # Pull all records back from form, but limited field list def doPullData_LimitedFields(bg_FormName, bg_Token): bgTokenType = 'AR-JWT' myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token} # Setup the list of fields only to return # *** BMC REST API unable to handle this and I consider it a defect on their side # Their documentation is incorrect #fieldList_params = {'fields': "values(Restaurant,Status)"} # Note that BMC documentation is wrong on this. The REST API on their # end is not able to decode the FIELDS, LIMIT, or SORT. # These must be appeneded to the URL. Only the Query can be urlencoded # and put into the 'params=' variable # *** Looks like everything inside of the ( ) needs to be encoded if any non # URL safe characters are used. Still not able to push this into the 'params=' # variable as the "values()" also gets encoded. The brackes must not be encoded # Javascript/Java may handle the pramameters a little differently. bgFields = "Restaurant,Status" bgTmp = myURL + "?fields=values(" + bgFields + ")" # Setup the qualfication. This automatically gets encoded via the 'params=' variable bgQuery = "'Restaurant' LIKE \"Test%\" AND 'Status' = \"Proposed\"" # Setup the search parameter to pass search_params = {'q': bgQuery} # Pull back all data in the form (The 'params=' will automcatially encode its contents) #myR = requests.get(url = myURL, headers = myHeaders, params=fieldList_params, verify=False) #myR = requests.get(url = myURL, headers = myHeaders, verify=False) # Using a modified version of the URL + Fields restriction myR = requests.get(url = bgTmp, headers = myHeaders, params=search_params, verify=False) print("URL Used:") print(myR.url) print("") if(myR.status_code != 200): print('Status code:', myR.status_code) print('Headers:', myR.headers) print('Error Response:', myR.json()) return(-1) bgData = myR.json() #print(bgData) # bgData contains a dictionay of two items: # entries # _links # Copy the dictionary 'entries' into variable avar = bgData['entries'] # len(avar) will tell us the number of returned records # Loop the number of records returned for x in range(len(avar)): # Grab the dictionary of 'values' myVals = avar[x]['values'] # Print out the key/value pair for keys,values in myVals.items(): print('{} => {}'.format(keys,values)) print("") return(len(avar)) def doPullSearchData(bg_FormName, bg_Token): bgTokenType = 'AR-JWT' myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token} myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName # Enter in your search criteria as you would in the Mid-tier advanced search # Dont forget to escape out your quotes... #bgQuery = "'Restaurant' = \"Test Snack Hut\"" #bgQuery = "'Restaurant' LIKE \"Test%\"" bgQuery = "'Restaurant' LIKE \"Test%\" AND 'Status' = \"Proposed\"" # Setup the search parameter to pass search_params = {'q': bgQuery} myR = requests.get(url=myURL, headers=myHeaders, verify=False, params=search_params) print("URL Used:") print(myR.url) print("") if(myR.status_code != 200): print('Status code:', myR.status_code) print('Headers:', myR.headers) print('Error Response:', myR.json()) return(-1) bgData = myR.json() # Copy the dictionary 'entries' into variable avar = bgData['entries'] # len(avar) will tell us the number of returned records # Loop the number of records returned for x in range(len(avar)): # Grab the dictionary of 'values' myVals = avar[x]['values'] # Print out the key/value pair for keys,values in myVals.items(): print('{} => {}'.format(keys,values)) print("") return(len(avar)) # Pull all data back from form def doPullSingleRequestIDData(bg_FormName, bg_RequestID, bg_Token): bgTokenType = 'AR-JWT' myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName + '/' + bg_RequestID myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token} # Pull back all data in the form myR = requests.get(url = myURL, headers = myHeaders, verify=False) print("URL Used:") print(myR.url) print("") if(myR.status_code != 200): print('Status code:', myR.status_code) print('Headers:', myR.headers) print('Error Response:', myR.json()) return(-1) bgData = myR.json() # bgData contains a dictionay of two items: # entries # _links #print(bgData) # Copy the dictionary 'entries' into variable avar = bgData['values'] # Print out the key/value pair for keys,values in avar.items(): print('{} => {}'.format(keys,values)) return(len(avar)) # Create a record in form def doCreateRecord(bg_FormName, bg_Token): bgTokenType = 'AR-JWT' myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token, 'Content-Type': 'application/json'} # Setting the data= like this will not work. Must be in the raw JSON format #payload = {'Restaurant':'Test Cheese House','Status':'Active','Average Cost/Person':12.99,'Assigned To':'Bob'} # Format of the data= information must use the following template: # { # "values":{ # "Restaurant":"Test MacEnCheese", <-- Start of the fields list to set # "Status":"Active", # "Average Cost/Person":2.99, # "Assigned To":"Bob" <-- End of the fields list to set # } # } payload = '{ \"values\":{\"Restaurant\":\"Test Pizza World\",\"Status\":\"Active\",\"Average Cost/Person\":2.99,\"Assigned To\":\"Bob\"} }' # Pull back all data in the form myR = requests.post(url = myURL, data=payload, headers = myHeaders, verify=False) if(myR.status_code != 201): print('Status code:', myR.status_code) print('Headers:', myR.headers) print('Error Response:', myR.json()) return(-1) print("URL to new record:") print(myR.headers['location']) print("") return(1) # Modify an Entry def doModifySingleRequestIDData(bg_FormName, bg_RequestID, bg_Token): bgTokenType = 'AR-JWT' myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName + '/' + bg_RequestID myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token, 'Content-Type':'application/json'} payload = '{ \"values\":{\"Status\":\"Inactive\",\"Average Cost/Person\":3.99,\"Assigned To\":\"Rick\"} }' # Pull back all data in the form myR = requests.put(url = myURL, headers = myHeaders, data=payload, verify=False) print("URL Used:") print(myR.url) print("") print("Status of Update: {}".format(myR.status_code)) # 204 = sucessful updateTime # 412 = unmodified (same data was pushed) if(myR.status_code != 204): if(myR.status_code != 412): # 403 = Forbidden # 404 = Form does not exist print('Status code:', myR.status_code) print("") print('Headers:', myR.headers) print("") print('Error Response:', myR.json()) return(-1) def getFormDefinition(bg_FormName, bg_Token): bgTokenType = 'AR-JWT' myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token} myR = requests.options(url = myURL, headers = myHeaders, verify=False) print("URL Used:") print(myR.url) print("") print("Status of call: {}".format(myR.status_code)) bgData = myR.json() # Print the raw data returned #print(bgData) # Drill down and only pull out the field information avar = bgData['properties']['values']['properties'] # Print the new raw data #print(avar) print("") print("-----Key Value pairs-----") print("") # Loop through all the fields returned for key in sorted(avar.keys()): print("Key: {}".format(key)) # Pull out a field to look at tmp = avar[key] # Print out the 'type' keys data. Exists for all fields print("Type: {}".format(tmp['type'])) # If the key exists, print it out if 'maxLength' in tmp: print("Length: {}".format(tmp['maxLength'])) if 'format' in tmp: print("Format: {}".format(tmp['format'])) if 'required' in tmp: print("Required: {}".format(tmp['required'])) print("") print("") print("-----------------------") print("") def main(): # Login and get token bgReturnVal, bgToken = doLogin() # Only continue if we logged in successfully if bgReturnVal == 1: # Pull all records #bgResult = doPullData("AP-Sample:Restaurant", bgToken) #if(bgResult == -1): # print("An error occured...") #else: # print("Found {} record(s) in our last query".format(bgResult)) # Pull all records back, but only certain fields #bgResult = doPullData_LimitedFields("AP-Sample:Restaurant", bgToken) # Pull records based on hard coded query #bgResult = doPullSearchData("AP-Sample:Restaurant", bgToken) # Pull record back based on its RequestID. This is a standard format #doPullSingleRequestIDData("AP-Sample:Restaurant", "000000000000105", bgToken) # Create a new recodrs in the form (hard coded values used) #bgResult = doCreateRecord("AP-Sample:Restaurant", bgToken) # Modify the record based on its RequestID. This is a standard format #doModifySingleRequestIDData("AP-Sample:Restaurant", "000000000000105", bgToken) # Get definitions of all the fields on form getFormDefinition("AP-Sample:Restaurant", bgToken) # Logout doLogout(bgToken) main()