Implement Chrome version history (#1354)

* Implement Chrome version history

* Update GamCommands.txt

* Use httpObj
This commit is contained in:
Ross Scroggs
2021-04-06 11:08:27 -07:00
committed by GitHub
parent b262c4a898
commit 1b1a0c876c
7 changed files with 716 additions and 21 deletions

View File

@@ -53,6 +53,7 @@ from gam import fileutils
from gam.gapi import calendar as gapi_calendar
from gam.gapi import cloudidentity as gapi_cloudidentity
from gam.gapi import cbcm as gapi_cbcm
from gam.gapi import chromehistory as gapi_chromehistory
from gam.gapi import chromemanagement as gapi_chromemanagement
from gam.gapi import chromepolicy as gapi_chromepolicy
from gam.gapi.cloudidentity import devices as gapi_cloudidentity_devices
@@ -893,14 +894,14 @@ def getValidOauth2TxtCredentials(force_refresh=False, api=None):
return credentials
def getService(api, http):
def getService(api, httpObj):
api, version, api_version = getAPIVersion(api)
if api in GM_Globals[GM_CURRENT_API_SERVICES] and version in GM_Globals[
GM_CURRENT_API_SERVICES][api]:
service = googleapiclient.discovery.build_from_document(
GM_Globals[GM_CURRENT_API_SERVICES][api][version], http=http)
GM_Globals[GM_CURRENT_API_SERVICES][api][version], http=httpObj)
if GM_Globals[GM_CACHE_DISCOVERY_ONLY]:
http.cache = None
httpObj.cache = None
return service
if api in V1_DISCOVERY_APIS:
discoveryServiceUrl = googleapiclient.discovery.DISCOVERY_URI
@@ -912,7 +913,7 @@ def getService(api, http):
service = googleapiclient.discovery.build(
api,
version,
http=http,
http=httpObj,
cache_discovery=False,
static_discovery=False,
discoveryServiceUrl=discoveryServiceUrl)
@@ -920,23 +921,25 @@ def getService(api, http):
GM_Globals[GM_CURRENT_API_SERVICES][api][
version] = service._rootDesc.copy()
if GM_Globals[GM_CACHE_DISCOVERY_ONLY]:
http.cache = None
httpObj.cache = None
return service
except (httplib2.ServerNotFoundError, RuntimeError) as e:
if n != retries:
http.connections = {}
httpObj.connections = {}
controlflow.wait_on_failure(n, retries, str(e))
continue
controlflow.system_error_exit(4, str(e))
except (googleapiclient.errors.InvalidJsonError, KeyError,
ValueError) as e:
http.cache = None
httpObj.cache = None
if n != retries:
controlflow.wait_on_failure(n, retries, str(e))
continue
controlflow.system_error_exit(17, str(e))
except (http_client.ResponseNotReady, OSError,
googleapiclient.errors.HttpError) as e:
if 'The request is missing a valid API key' in str(e):
break
if n != retries:
controlflow.wait_on_failure(n, retries, str(e))
continue
@@ -946,12 +949,12 @@ def getService(api, http):
disc_file, discovery = readDiscoveryFile(api_version)
try:
service = googleapiclient.discovery.build_from_document(discovery,
http=http)
http=httpObj)
GM_Globals[GM_CURRENT_API_SERVICES].setdefault(api, {})
GM_Globals[GM_CURRENT_API_SERVICES][api][
version] = service._rootDesc.copy()
if GM_Globals[GM_CACHE_DISCOVERY_ONLY]:
http.cache = None
httpObj.cache = None
return service
except (KeyError, ValueError):
controlflow.invalid_json_exit(disc_file)
@@ -961,9 +964,9 @@ def buildGAPIObject(api):
GM_Globals[GM_CURRENT_API_USER] = None
credentials = getValidOauth2TxtCredentials(api=getAPIVersion(api)[0])
credentials.user_agent = GAM_INFO
http = transport.AuthorizedHttp(
httpObj = transport.AuthorizedHttp(
credentials, transport.create_http(cache=GM_Globals[GM_CACHE_DIR]))
service = getService(api, http)
service = getService(api, httpObj)
if GC_Values[GC_DOMAIN]:
if not GC_Values[GC_CUSTOMER_ID]:
resp, result = service._http.request(
@@ -995,6 +998,12 @@ def buildGAPIObject(api):
return service
def buildGAPIObjectNoAuthentication(api):
GM_Globals[GM_CURRENT_API_USER] = None
httpObj = transport.create_http(cache=GM_Globals[GM_CACHE_DIR])
service = getService(api, httpObj)
return service
# Convert UID to email address
def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, email_types=['user']):
if isinstance(email_types, str):
@@ -1078,23 +1087,23 @@ def convertEmailAddressToUID(emailAddressOrUID, cd=None, email_type='user'):
def buildGAPIServiceObject(api, act_as, showAuthError=True):
http = transport.create_http(cache=GM_Globals[GM_CACHE_DIR])
service = getService(api, http)
httpObj = transport.create_http(cache=GM_Globals[GM_CACHE_DIR])
service = getService(api, httpObj)
GM_Globals[GM_CURRENT_API_USER] = act_as
GM_Globals[GM_CURRENT_API_SCOPES] = API_SCOPE_MAPPING.get(
api, service._rootDesc['auth']['oauth2']['scopes'])
credentials = getSvcAcctCredentials(GM_Globals[GM_CURRENT_API_SCOPES],
act_as)
request = transport.create_request(http)
request = transport.create_request(httpObj)
retries = 3
for n in range(1, retries + 1):
try:
credentials.refresh(request)
service._http = transport.AuthorizedHttp(credentials, http=http)
service._http = transport.AuthorizedHttp(credentials, http=httpObj)
break
except (httplib2.ServerNotFoundError, RuntimeError) as e:
if n != retries:
http.connections = {}
httpObj.connections = {}
controlflow.wait_on_failure(n, retries, str(e))
continue
controlflow.system_error_exit(4, e)
@@ -11533,6 +11542,8 @@ def ProcessGAMCommand(args):
gapi_chromemanagement.printAppDevices()
elif argument in ['chromeversions']:
gapi_chromemanagement.printVersions()
elif argument in ['chromehistory']:
gapi_chromehistory.printHistory()
else:
controlflow.invalid_argument_exit(argument, 'gam print')
sys.exit(0)

View File

@@ -0,0 +1,153 @@
"""Chrome Version History API calls"""
import sys
import gam
from gam.var import *
from gam import controlflow
from gam import display
from gam import gapi
from gam import utils
def build():
return gam.buildGAPIObjectNoAuthentication('versionhistory')
CHROME_HISTORY_ENTITY_CHOICES = {
'platforms',
'channels',
'versions',
'releases',
}
CHROME_PLATFORM_CHOICE_MAP = {
'all': 'all',
'android': 'android',
'ios': 'ios',
'lacros': 'lacros',
'linux': 'linux',
'mac': 'mac',
'macarm64': 'mac_arm64',
'sebview': 'webview',
'win': 'win',
'win64': 'win64',
}
CHROME_CHANNEL_CHOICE_MAP = {
'beta': 'beta',
'canary': 'canary',
'canaryasan': 'canary_asan',
'dev': 'dev',
'stable': 'stable',
}
CHROME_VERSIONHISTORY_ORDERBY_CHOICE_MAP = {
'versions': {
'channel': 'channel',
'name': 'name',
'platform': 'platform',
'version': 'version'
},
'releases': {
'channel': 'channel',
'endtime': 'endtime',
'fraction': 'fraction',
'name': 'name',
'platform': 'platform',
'starttime': 'starttime',
'version': 'version'
}
}
CHROME_VERSIONHISTORY_TITLES = {
'platforms': ['name', 'platformType'],
'channels': ['name', 'channelType'],
'versions': ['name', 'version'],
'releases': ['name', 'version', 'fraction', 'serving.startTime', 'serving.endTime']
}
def printHistory():
cv = build()
entityType = sys.argv[3].lower().replace('_', '')
if entityType not in CHROME_HISTORY_ENTITY_CHOICES:
msg = f'{entityType} is not a valid argument to "gam print chromehistory"'
controlflow.system_error_exit(3, msg)
todrive = False
csvRows = []
cplatform = 'all'
channel = 'all'
version = 'all'
kwargs = {}
orderByList = []
i = 4
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'todrive':
todrive = True
i += 1
elif entityType != 'platforms' and myarg == 'platform':
cplatform = sys.argv[i + 1].lower().replace('_', '')
if cplatform not in CHROME_PLATFORM_CHOICE_MAP:
controlflow.expected_argument_exit('platform',
', '.join(CHROME_PLATFORM_CHOICE_MAP),
cplatform)
cplatform = CHROME_PLATFORM_CHOICE_MAP[cplatform]
i += 2
elif entityType in {'versions', 'releases'} and myarg == 'channel':
channel = sys.argv[i + 1].lower().replace('_', '')
if channel not in CHROME_CHANNEL_CHOICE_MAP:
controlflow.expected_argument_exit('channel',
', '.join(CHROME_CHANNEL_CHOICE_MAP),
channel)
channel = CHROME_CHANNEL_CHOICE_MAP[channel]
i += 2
elif entityType == 'releases' and myarg == 'version':
version = sys.argv[i + 1]
i += 2
elif entityType in {'versions', 'releases'} and myarg == 'orderby':
fieldName = sys.argv[i + 1].lower().replace('_', '')
i += 2
if fieldName in CHROME_VERSIONHISTORY_ORDERBY_CHOICE_MAP[entityType]:
fieldName = CHROME_VERSIONHISTORY_ORDERBY_CHOICE_MAP[entityType][fieldName]
orderBy = ''
if i < len(sys.argv):
orderBy = sys.argv[i].lower()
if orderBy in SORTORDER_CHOICES_MAP:
orderBy = SORTORDER_CHOICES_MAP[orderBy]
i += 1
if orderBy != 'DESCENDING':
orderByList.append(fieldName)
else:
orderByList.append(f'{fieldName} desc')
else:
controlflow.expected_argument_exit('orderby',
', '.join(CHROME_VERSIONHISTORY_ORDERBY_CHOICE_MAP[entityType]),
fieldName)
elif entityType in {'versions', 'releases'} and myarg == 'filter':
kwargs['filter'] = sys.argv[i + 1]
i += 2
else:
msg = f'{myarg} is not a valid argument to "gam print chromehistory {entityType}"'
controlflow.system_error_exit(3, msg)
if orderByList:
kwargs['orderBy'] = ','.join(orderByList)
if entityType == 'platforms':
svc = cv.platforms()
parent = 'chrome'
elif entityType == 'channels':
svc = cv.platforms().channels()
parent = f'chrome/platforms/{cplatform}'
elif entityType == 'versions':
svc = cv.platforms().channels().versions()
parent = f'chrome/platforms/{cplatform}/channels/{channel}'
else: #elif entityType == 'releases'
svc = cv.platforms().channels().versions().releases()
parent = f'chrome/platforms/{cplatform}/channels/{channel}/versions/{version}'
reportTitle = f'Chrome Version History {entityType.capitalize()}'
page_message = gapi.got_total_items_msg(reportTitle, '...\n')
gam.printGettingAllItems(reportTitle, None)
citems = gapi.get_all_pages(svc, 'list', entityType,
page_message=page_message,
parent=parent,
fields=f'nextPageToken,{entityType}',
**kwargs)
for citem in citems:
csvRows.append(utils.flatten_json(citem))
display.write_csv_file(csvRows, CHROME_VERSIONHISTORY_TITLES[entityType], reportTitle, todrive)

View File

@@ -40,19 +40,19 @@ def create_http(cache=None,
return httpObj
def create_request(http=None):
def create_request(httpObj=None):
"""Creates a uniform Request object with a default http, if not provided.
Args:
http: Optional httplib2.Http compatible object to be used with the request.
httpObj: Optional httplib2.Http compatible object to be used with the request.
If not provided, a default HTTP will be used.
Returns:
Request: A google_auth_httplib2.Request compatible Request.
"""
if not http:
http = create_http()
return Request(http)
if not httpObj:
httpObj = create_http()
return Request(httpObj)
GAM_USER_AGENT = GAM_INFO

View File

@@ -324,6 +324,7 @@ API_VER_MAPPING = {
'siteVerification': 'v1',
'storage': 'v1',
'vault': 'v1',
'versionhistory': 'v1',
}
USERINFO_EMAIL_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'