diff --git a/.gitignore b/.gitignore index df2929e..4d084a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .csv +.vscode/ +.DS_Store \ No newline at end of file diff --git a/elf.py b/elf.py index 5b1299d..5d6f786 100755 --- a/elf.py +++ b/elf.py @@ -1,106 +1,80 @@ -#!/usr/bin/python -''' -# Python 2.7.9 script to download EventLogFiles -# Pre-requisite: standard library functionality = e.g urrlib2, json, StringIO - - #/** - #* Copyright (c) 2012, Salesforce.com, Inc. All rights reserved. - #* - #* Redistribution and use in source and binary forms, with or without - #* modification, are permitted provided that the following conditions are - #* met: - #* - #* * Redistributions of source code must retain the above copyright - #* notice, this list of conditions and the following disclaimer. - #* - #* * Redistributions in binary form must reproduce the above copyright - #* notice, this list of conditions and the following disclaimer in - #* the documentation and/or other materials provided with the - #* distribution. - #* - #* * Neither the name of Salesforce.com nor the names of its - #* contributors may be used to endorse or promote products derived - #* from this software without specific prior written permission. - #* - #* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - #* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - #* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - #* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - #* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - #* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - #* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - #* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - #* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - #* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - #* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #*/ -''' # Connected App information: fill it in by creating a connected app # https://help.salesforce.com/articleView?id=connected_app_create.htm&language=en_US&type=0 -CLIENT_ID = 'FILL_ME_IN' -CLIENT_SECRET = 'FILL_ME_IN' #Imports - -import urllib2 +import urllib.request import json -#import ssl +import ssl import getpass import os import sys import gzip import time -from StringIO import StringIO +from io import StringIO import base64 # login function def login(): ''' Login to salesforce service using OAuth2 ''' - # prompt for username and password - username = raw_input('Username: \n') - password = getpass.getpass('Password: \n') + # username = '' + # password = '' + # clientId = '' + # clientSecret = '' + # prompt for username, password, consumer key and consumer secret + # while len(username) < 1: + print('Remember, you need data from a Connected App') + username = input('Username: ') + # while len(password) < 1: + password = getpass.getpass('Password and Token:') + # while len(clientId) < 1: + clientId = input('Consumer key: ') + # while len(clientSecret) < 1: + clientSecret = input('Consumer secret: ') + instanceType = input('Instance type (login by default): ') + redirectURI = input('Redirect URI (from your App Connected, "http://localhost:4200/" by default): ') # check to see if anything was entered and if not, default values # change default values for username and password to your own if len(username) < 1: - username = 'user@company.com' - password = 'Passw0rd' - print 'Using default username: ' + username + username = 'your@email.com' + password = 'PasswordToken' + clientId = 'YourConsumerKey' + clientSecret = 'YourClientSecret' + redirectURI = 'http://localhost:4200/' + print('Using default username and credentials: {0}'.format(username)) else: - print 'Using user inputed username: ' + username + print('Using user inputed username and credentials: {0}'.format(username)) + # Use 'login' by default + if len(instanceType) < 1: + instanceType = 'login' - print 'check point' + print('check point') # create a new salesforce REST API OAuth request - url = 'https://login.salesforce.com/services/oauth2/token' - data = '&grant_type=password&client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&username='+username+'&password='+password + url = 'https://' + instanceType + '.salesforce.com/services/oauth2/token' + dataUnencoded = { + 'grant_type': 'password', + 'client_id': clientId, + 'client_secret': clientSecret, + 'redirect_uri': redirectURI, + 'username': username, + 'password': password + } + data = urllib.parse.urlencode(dataUnencoded).encode("utf-8") headers = {'X-PrettyPrint' : '1'} - - # workaround to ssl issue introduced before version 2.7.9 - #if hasattr(ssl, '_create_unverified_context'): - #ssl._create_default_https_context = ssl._create_unverified_context - - # These lines are for when you have a proxy server - # uncomment the next line to work with a local proxy server. replace the URL with the URL of your proxy - # proxy = urllib2.ProxyHandler({'https': 'http://127.0.0.1:8888/'}) - # uncomment the next two lines if your proxy needs authentication. replace 'realm' through 'password' with appropriate values. 'realm' is often null - # proxy_auth_handler = urllib2.HTTPBasicAuthHandler() - # proxy_auth_handler.add_password('realm', 'host', 'username', 'password') - # pick one of the next two lines based on whether you need proxy authentication. The first is authenticated, the second is unauthenticated - # opener = urllib2.build_opener(proxy, proxy_auth_handler) - # opener = urllib2.build_opener(proxy) - # uncomment the final line to enable the proxy for any calls - # urllib2.install_opener(opener) - # call salesforce REST API and pass in OAuth credentials - req = urllib2.Request(url, data, headers) - res = urllib2.urlopen(req) - - # load results to dictionary - res_dict = json.load(res) - - # close connection - res.close() + req = urllib.request.Request(url, data, headers = headers) + try: + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + res = urllib.request.urlopen(req, context = ctx) + res_dict = json.load(res) + res.close() + except urllib.error.URLError as e: + if(e.reason == 'Bad Request'): + print('Error: {0}, check input data'.format(e.reason)) + else: + print('Error: {0}'.format(e.reason)) + sys.exit() # return OAuth access token necessary for additional REST API calls access_token = res_dict['access_token'] @@ -114,20 +88,20 @@ def download_elf(): # login and retrieve access_token and day access_token, instance_url = login() - day = raw_input('\nDate range (e.g. Last_n_Days:2, Today, Tomorrow):\n') + day = input('\nDate range (e.g. Last_n_Days:2, Today, Tomorrow):\n') # check to see if anything was entered and if not, default values if len(day) < 1: day = 'Last_n_Days:2' - print 'Using default date range: ' + day + '\n' + print('Using default date range: {0} \n'.format(day)) else: - print 'Using user inputed date range: ' + day + '\n' + print('Using user inputed date range: {0} \n'.format(day)) # query Ids from Event Log File - url = instance_url+'/services/data/v33.0/query?q=SELECT+Id+,+EventType+,+Logdate+From+EventLogFile+Where+LogDate+=+'+day + url = instance_url+'/services/data/v41.0/query?q=SELECT+Id+,+EventType+,+Logdate+From+EventLogFile+Where+LogDate+=+'+day headers = {'Authorization' : 'Bearer ' + access_token, 'X-PrettyPrint' : '1'} - req = urllib2.Request(url, None, headers) - res = urllib2.urlopen(req) + req = urllib.request.Request(url, None, headers = headers) + res = urllib.request.urlopen(req) res_dict = json.load(res) # capture record result size to loop over @@ -135,18 +109,18 @@ def download_elf(): # provide feedback if no records are returned if total_size < 1: - print 'No records were returned for ' + day + print('No records were returned for {0}'.format(day)) sys.exit() # create a directory for the output - dir = raw_input("Output directory: ") + dir = input("Output directory: ") # check to see if anything if len(dir) < 1: dir = 'elf' - print '\ndefault directory name used: ' + dir + print('\ndefault directory name used: {0}'.format(dir)) else: - print '\ndirectory name used: ' + dir + print('\ndirectory name used: {0}'.format(dir)) # If directory doesn't exist, create one if not os.path.exists(dir): @@ -156,15 +130,17 @@ def download_elf(): res.close # check to see if the user wants to download it compressed - compress = raw_input('\nUse compression (y/n)\n').lower() - print compress + # compress = input('\nUse compression (y/n)\n').lower() + # print(compress) + # Disabled compress validation meanwhile... + compress = 'n' # check to see if anything if len(compress) < 1: compress = 'yes' - print '\ndefault compression being used: ' + compress + print('\ndefault compression being used: {0}'.format(compress)) else: - print '\ncompression being used: ' + compress + print('\ncompression being used: {0}'.format(compress)) # loop over json elements in result and download each file locally for i in range(total_size): @@ -174,27 +150,27 @@ def download_elf(): dates = res_dict['records'][i]['LogDate'] # create REST API request - url = instance_url+'/services/data/v33.0/sobjects/EventLogFile/'+ids+'/LogFile' + url = instance_url+'/services/data/v41.0/sobjects/EventLogFile/'+ids+'/LogFile' # provide correct compression header if (compress == 'y') or (compress == 'yes'): headers = {'Authorization' : 'Bearer ' + access_token, 'X-PrettyPrint' : '1', 'Accept-encoding' : 'gzip'} - print 'Using gzip compression\n' + print('Using gzip compression\n') else: headers = {'Authorization' : 'Bearer ' + access_token, 'X-PrettyPrint' : '1'} - print 'Not using gzip compression\n' + print('Not using gzip compression\n') # begin profiling start = time.time() # open connection - req = urllib2.Request(url, None, headers) - res = urllib2.urlopen(req) + req = urllib.request.Request(url, None, headers) + res = urllib.request.urlopen(req) - print '********************************' + print('********************************') # provide feedback to user - print 'Downloading: ' + dates[:10] + '-' + types + '.csv to ' + os.getcwd() + '/' + dir + '\n' + print('Downloading: ' + dates[:10] + '-' + types + '.csv to ' + os.getcwd() + '/' + dir + '\n') # print the response to see the content type # print res.info() @@ -203,7 +179,11 @@ def download_elf(): # compression code from http://bit.ly/pyCompression if res.info().get('Content-Encoding') == 'gzip': # buffer results - buf = StringIO(res.read()) + html = res.read() + decodedHtml = html.decode('iso-8859-1')#.encode('UTF-8') + # decodedHtml = decodedHtml.decode('UTF-8') + print('HTML=>', decodedHtml) + buf = StringIO(decodedHtml) # gzip decode the response f = gzip.GzipFile(fileobj=buf) # print data @@ -212,7 +192,7 @@ def download_elf(): buf.close() else: # buffer results - buf = StringIO(res.read()) + buf = StringIO(res.read().decode('utf-8')) # get the value from the buffer data = buf.getvalue() #print data @@ -228,7 +208,7 @@ def download_elf(): #msecs = secs * 1000 # millisecs #print 'elapsed time: %f ms' % msecs - print 'Total download time: %f seconds\n' % secs + print('Total download time: %f seconds\n' % secs) file.close i = i + 1 @@ -236,4 +216,4 @@ def download_elf(): # close connection res.close -download_elf() +download_elf() \ No newline at end of file