diff --git a/sharedfunctions.py b/sharedfunctions.py index 4d8b050..d89a75f 100755 --- a/sharedfunctions.py +++ b/sharedfunctions.py @@ -14,16 +14,16 @@ # # Change History # -# 27JAN2018 Comments added -# 29JAN2018 Added simpleresults function -# 31JAN2018 Added the ability to pass contenttype to call_rest_api (now callrestapi) +# 27JAN2018 Comments added +# 29JAN2018 Added simpleresults function +# 31JAN2018 Added the ability to pass contenttype to call_rest_api (now callrestapi) # 31JAN2018 Improved error handling of call_rest_api (now callrestapi) -# 31JAN2018 Deal with situation where json is not returned +# 31JAN2018 Deal with situation where json is not returned # 31JAN2018 Fix a bug when neither json or text is returned -# 02FEB2018 Fix a bug when text is returned -# 12MAR2018 Made simple result print generic -# 20MAR2018 Added some comments -# 20MAR2018 Handle errors when profile and authentication token do not exist +# 02FEB2018 Fix a bug when text is returned +# 12MAR2018 Made simple result print generic +# 20MAR2018 Added some comments +# 20MAR2018 Handle errors when profile and authentication token do not exist # 20May2018 Fixed bug in authentication check # 01jun2018 Deal with empty profile error # 23oct2018 Added print result function @@ -62,23 +62,23 @@ pp = pprint.PrettyPrinter(indent=4) # validate rest api is not used at this time # not used - + def validaterestapi(baseurl, reqval, reqtype, data={}): - + global result print("The request is a "+reqtype+" request: ",baseurl+reqval) - + json_data=json.dumps(data, ensure_ascii=False) - + print("Data for Request:") print(json_data) - - if (reqtype !="get" or reqtype !="post" or reqtype!="delete" or reqtype!="put"): + + if (reqtype !="get" or reqtype !="post" or reqtype!="delete" or reqtype!="put"): print("NOTE: Invalid method") - + return; - + # callrestapi # this is the main function called many other programs and by the callrestapi program to make the REST calls # change history @@ -86,26 +86,26 @@ def validaterestapi(baseurl, reqval, reqtype, data={}): # 28oct2018 Added stop on error to be able to override stopping processing when an error occurs def callrestapi(reqval, reqtype, acceptType='application/json', contentType='application/json',data={},stoponerror=1): - - + + # get the url from the default profile baseurl=getbaseurl() # get the auth token oaval=getauthtoken(baseurl) - + # build the authorization header head= {'Content-type':contentType,'Accept':acceptType} head.update({"Authorization" : oaval}) - - # maybe this can be removed + + # maybe this can be removed global result # sereliaze the data string for the request to json format json_data=json.dumps(data, ensure_ascii=False) - - # call the rest api using the parameters passed in and the requests python library - + + # call the rest api using the parameters passed in and the requests python library + if reqtype=="get": ret = requests.get(baseurl+reqval,headers=head,data=json_data) elif reqtype=="post": @@ -117,20 +117,20 @@ def callrestapi(reqval, reqtype, acceptType='application/json', contentType='app else: result=None print("NOTE: Invalid method") - sys.exit() - - - # response error if status code between these numbers + sys.exit() + + + # response error if status code between these numbers if (400 <= ret.status_code <=599): - - print(ret.text) - result=None - if stoponerror: sys.exit() - - # return the result + + print(ret.text) + result=None + if stoponerror: sys.exit() + + # return the result else: - # is it json - try: + # is it json + try: result=ret.json() except: # is it text @@ -139,11 +139,11 @@ def callrestapi(reqval, reqtype, acceptType='application/json', contentType='app except: result=None print("NOTE: No result to print") - - - + + + return result; - + # getfolderid # when a Viya content path is passed in return the id, path and uri # change history @@ -151,11 +151,11 @@ def callrestapi(reqval, reqtype, acceptType='application/json', contentType='app # 08Feb2020 return full json as 4 item in list that is returned def getfolderid(path): - - # build the request parameters + + # build the request parameters reqval="/folders/folders/@item?path="+path reqtype='get' - + callrestapi(reqval,reqtype) if result==None: @@ -167,10 +167,10 @@ def getfolderid(path): targetid=result['id'] targetname=result['name'] targeturi="/folders/folders/"+targetid - + return [targetid,targeturi,targetname,result] - - + + # getbaseurl # from the default profile return the baseurl of the Viya server # change history @@ -178,7 +178,7 @@ def getfolderid(path): # 01jun2018 Deal with empty profile error # 20nov2018 Use the SAS_CLI_PROFILE env variable - + def getbaseurl(): # check that profile file is available and can be read @@ -200,13 +200,13 @@ def getbaseurl(): # get json from profile with open(endpointfile) as json_file: data = json.load(json_file) - + # get the profile environment variable to use it # if it is not set default to the default profile - + cur_profile=os.environ.get("SAS_CLI_PROFILE","Default") #print("URL: ",cur_profile ) - + # check that information is in profile if cur_profile in data: baseurl=data[cur_profile]['sas-endpoint'] @@ -221,57 +221,57 @@ def getbaseurl(): # getauthtoken -# from the stored auth file get the authentication token for the request header +# from the stored auth file get the authentication token for the request header # change history # 01dec2017 initial development # return oaval=None when no authtoken retrieved # 20nov2018 Use the SAS_CLI_PROFILE env variable def getauthtoken(baseurl): - - + + #get authentication information for the header credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json') - + # check that credential file is available and can be read access_file=file_accessible(credential_file,'r') - + if access_file==False: oaval=None print("ERROR: Cannot read authentication credentials at: ", credential_file) print("ERROR: Try refreshing your token with sas-admin auth login") sys.exit() - - with open(credential_file) as json_file: + + with open(credential_file) as json_file: data = json.load(json_file) type(data) - + # the sas-admin profile init creates an empty credential file # check that credential is in file, if it is add it to the header, if not exit - + # get the profile environment variable to use it # if it is not set default to the default profile - + cur_profile=os.environ.get("SAS_CLI_PROFILE","Default") - + #print("LOGON: ", cur_profile ) - + if cur_profile in data: - + oauthToken=data[cur_profile]['access-token'] - + oauthTokenType="bearer" - + oaval=oauthTokenType + ' ' + oauthToken - + head= {'Content-type':'application/json','Accept':'application/json' } head.update({"Authorization" : oaval}) - + # test a connection to rest api if it fails exit r = requests.get(baseurl,headers=head) - + if (400 <= r.status_code <=599): - + oaval=None print(r.text) print("ERROR: cannot connect to "+baseurl+" is your token expired?") @@ -283,69 +283,69 @@ def getauthtoken(baseurl): print("ERROR: access token not in file: ", credential_file) print("ERROR: Try refreshing your token with sas-admin auth login") sys.exit() - + return oaval - + # getinputjson # load the returned json to a python dictionary # change history # 01dec2017 initial development - + def getinputjson(input_file): with open(input_file) as json_file: inputdata = json.load(json_file) - return inputdata - + return inputdata + # simpleresults # take the complex json and create a simple print of the results # change history # 01dec2017 initial development # 20dec2018 simple output now alphibetical order by key - + def simpleresults(resultdata): # print a simplification of the json results - - + + # list of items returned by rest call if 'items' in resultdata: - + total_items=resultdata['count'] - + returned_items=len(resultdata['items']) - + if total_items == 0: print("Note: No items returned.") - - for i in range(0,returned_items): - + + for i in range(0,returned_items): + print ("=====Item ",i,"=======") - + origpairs=resultdata['items'][i] - - test=origpairs.get('description') + + test=origpairs.get('description') if test==None: origpairs['description']='None' - + pairs=collections.OrderedDict(sorted(origpairs.items())) - - + + for key,val in pairs.items(): - - if key != 'links': + + if key != 'links': print(key,end="") print(" = ", val) - + print("Result Summary: Total items available: ",total_items ,"Total items returned: ", returned_items) - + elif 'id' in resultdata: #one item returned by rest call - + for key,val in resultdata.items(): - - if key != 'links': + + if key != 'links': print(key,end="") print(" = ", val) - + else: print("NOTE: No JSON Results Found") @@ -355,123 +355,123 @@ def simpleresults(resultdata): # take the complex json and create a simple table of the results # change history # 01aug2018 initial development -# 19dece2018 print csv in column orderwith only common columns +# 19dece2018 print csv in column orderwith only common columns def csvresults(resultdata,columns=[]): - + if 'items' in resultdata: - + total_items=resultdata['count'] - + returned_items=len(resultdata['items']) - + if total_items == 0: print("Note: No items returned.") - - for i in range(0,returned_items): - - + + for i in range(0,returned_items): + + origpairs=resultdata['items'][i] - + # create an ordered dictionary pairs=collections.OrderedDict() - + # loop thru the column list and insert to a new dictionary in that order # this ensures that colums appear in this order in the csv for keylabel in columns: - + # get the value for the current column curval=origpairs.get(keylabel) - + if curval != None: pairs[keylabel] = curval else: - pairs[keylabel] = 'None' - + pairs[keylabel] = 'None' + numvals=len(columns) z=0 - + # print header row of column names for key,val in pairs.items(): - - z=z+1 - + + z=z+1 + # seperate with comma except last item if z==numvals: sep='' else: sep=',' - + if i==0 and key in columns: print(key,sep,end="") - + print("\n",end="") - + z=0 - + # print rows for key,val in pairs.items(): - + # seperate with comma except last item z=z+1 if z==numvals: sep='' else: sep=',' - + if key != 'links' and key in columns: print('"'+str(val)+'"'+sep, end="") - - + + print("\n",end="") - - + + elif 'id' in resultdata: #one item returned by rest call - + numvals=len(resultdata.items()) z=0 - + for key,val in resultdata.items(): - + # seperate with comma except last item z=z+1 if z==numvals: sep='' else: sep=',' - + if key != 'links': print(key,sep,end="") - + print("\n",end="") - - + + z=0 - + for key,val in resultdata.items(): - + # seperate with comma except last item z=z+1 if z==numvals: sep='' else: sep=',' - + if key != 'links': print('"'+str(val)+'"'+sep,end="") - + print("\n",end="") - + else: print("NOTE: No JSON Results Found") # file_accessible -# Check if a file exists and is accessible. +# Check if a file exists and is accessible. # change history # 01dec2017 initial development - + def file_accessible(filepath, mode): - + try: f = open(filepath, mode) f.close() except IOError as e: return False - + return True # printresult -# prints the results in the style requested +# prints the results in the style requested # change history # 28oct2018 initial development # 22dec2018 add csv columns only relevent for csv output, defaults provided but can be overriden when called @@ -479,10 +479,10 @@ def file_accessible(filepath, mode): def printresult(result,output_style,colsforcsv=["id","name","type","description","creationTimeStamp","modifiedTimeStamp"]): - + # print rest call results if type(result) is dict: - + if output_style=='simple': simpleresults(result) elif output_style=='simplejson': @@ -491,54 +491,54 @@ def printresult(result,output_style,colsforcsv=["id","name","type","description" csvresults(result,columns=colsforcsv) else: print(json.dumps(result,indent=2)) - else: print(result) - + else: print(result) + # getprofileinfo # prints the token expiration, endpoint and current user # change history # 20nov2018 initial development - + def getprofileinfo(myprofile): - - - + + + #get authentication information for the header credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json') - + # check that credential file is available and can be read access_file=file_accessible(credential_file,'r') - + if access_file==False: print("ERROR: Cannot read authentication credentials at: ", credential_file) print("ERROR: Try refreshing your token with sas-admin auth login") sys.exit() - - with open(credential_file) as json_file: + + with open(credential_file) as json_file: data = json.load(json_file) type(data) - + # the sas-admin profile init creates an empty credential file # check that credential is in file, if it is add it to the header, if not exit - + # get the profile environment variable to use it # if it is not set default to the default profile - - + + if myprofile in data: - + expiry=data[myprofile]['expiry'] print("Note your authentication token expires at: "+expiry) - + else: - + print("ERROR: access token not in file: ", credential_file) print("ERROR: Try refreshing your token with sas-admin auth login") sys.exit() - - + + # note the path to the profile is hard-coded right now endpointfile=os.path.join(os.path.expanduser('~'),'.sas','config.json') access_file=file_accessible(endpointfile,'r') @@ -556,30 +556,30 @@ def getprofileinfo(myprofile): # get json from profile with open(endpointfile) as json_file: data = json.load(json_file) - - + + # check that information is in profile if myprofile in data: baseurl=data[myprofile]['sas-endpoint'] print("Endpoint is: "+baseurl) else: print("ERROR: profile "+myprofile+" does not exist. Recreate profile with sas-admin profile init.") - - # build the request parameters + + # build the request parameters reqval="/identities/users/@currentUser" reqtype='get' - + result=callrestapi(reqval,reqtype) - + if result==None: print("NOTE: Not logged in.") - + else: print("Logged on as id: "+ result['id']) print("Logged on as name: "+result['name']) - - - + + + # getpath # when a Viya objectURI is passed in return the path # change history @@ -611,7 +611,7 @@ def getpath(objecturi): return path # getidsanduris -# given a result json structure, return a dictionary with a list of id's and uri's +# given a result json structure, return a dictionary with a list of id's and uri's # change history # 01dec2017 initial development @@ -620,50 +620,65 @@ def getidsanduris(resultdata): resultdict={} resultdict['ids']=[] resultdict['uris']=[] - + # loop the result and add a list of ids and uris to the returned dictionary if 'items' in resultdata: - + total_items=resultdata['count'] returned_items=len(resultdata['items']) if total_items == 0: print("Note: No items returned.") - - for i in range(0,returned_items): - + + for i in range(0,returned_items): + resultdict['ids'].append(resultdata['items'][i]['id']) resultdict['uris'].append(resultdata['items'][i]['uri']) - + return resultdict # simplejsonresults # given a result json structure, remove all the "links" items -# this will return a more readable json output +# this will return a more readable json output # change history # 20feb2020 initial development - + def simplejsonresults(resultdata): - + if 'items' in resultdata: # list of items returned by rest call - - for key in list(resultdata): - if key == 'links': del resultdata[key] + + for key in list(resultdata): + if key == 'links': del resultdata[key] total_items=resultdata['count'] returned_items=len(resultdata['items']) - + if total_items == 0: print("Note: No items returned.") - - for i in range(0,returned_items): - + + for i in range(0,returned_items): + for key in list(resultdata['items'][i]): - if key=='links': + if key=='links': del resultdata['items'][i][key] - + print(json.dumps(resultdata,indent=2)) elif 'id' in resultdata: #one item returned by rest call - del resultdata['links'] + del resultdata['links'] print(json.dumps(resultdata,indent=2)) + + +def getapplicationproperties: + + # get the path for the script file this is where the properties file will bbe + thepath=os.path.split(inspect.getsourcefile(lambda:0)) + install_dir=thepath[0] + prop_file=os.path.join(install_dir, "application.properties") + + myparams=dict(line.strip().split('=') for line in open(prop_file) if line[0].isalpha()) + print(myparams) + + return myparms + +