blob: d8545ac679a8e8f21b38d27cdde3f0a259b53896 [file] [log] [blame]
xixuan878b1eb2017-03-20 15:58:17 -07001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module for interacting with google APIs."""
Xixuan Wu5d6063e2017-09-05 16:15:07 -07006# pylint: disable=g-bad-import-order
7
8import httplib2
xixuan878b1eb2017-03-20 15:58:17 -07009
10import apiclient
xixuan878b1eb2017-03-20 15:58:17 -070011import constants
12import file_getter
13
14from oauth2client import service_account
15from oauth2client.contrib import appengine
16
17
18class RestClientError(Exception):
Xixuan Wu5d6063e2017-09-05 16:15:07 -070019 """Raised when there is a general error."""
xixuan878b1eb2017-03-20 15:58:17 -070020
21
22class NoServiceRestClientError(RestClientError):
Xixuan Wu5d6063e2017-09-05 16:15:07 -070023 """Raised when there is no ready service for a google API."""
xixuan878b1eb2017-03-20 15:58:17 -070024
25
26class BaseRestClient(object):
Xixuan Wu5d6063e2017-09-05 16:15:07 -070027 """Base class of REST client for google APIs."""
xixuan878b1eb2017-03-20 15:58:17 -070028
Xixuan Wu5d6063e2017-09-05 16:15:07 -070029 def __init__(self, scopes, service_name, service_version):
30 """Initialize a REST client to connect to a google API.
xixuan878b1eb2017-03-20 15:58:17 -070031
Xixuan Wu5d6063e2017-09-05 16:15:07 -070032 Args:
33 scopes: the scopes of the to-be-connected API.
34 service_name: the service name of the to-be-connected API.
35 service_version: the service version of the to-be-connected API.
36 """
37 self.running_env = constants.environment()
38 self.scopes = scopes
39 self.service_name = service_name
40 self.service_version = service_version
xixuan878b1eb2017-03-20 15:58:17 -070041
Xixuan Wu5d6063e2017-09-05 16:15:07 -070042 @property
43 def service(self):
44 if not self._service:
45 raise NoServiceRestClientError('No service created for calling API')
xixuan878b1eb2017-03-20 15:58:17 -070046
Xixuan Wu5d6063e2017-09-05 16:15:07 -070047 return self._service
xixuan878b1eb2017-03-20 15:58:17 -070048
Xixuan Wu5d6063e2017-09-05 16:15:07 -070049 def create_service(self, discovery_url=None):
50 """Create the service for a google API."""
51 self._init_credentials()
52 # Explicitly specify timeout for http to avoid DeadlineExceededError.
53 # It's used for services like AndroidBuild API, which raise such error
54 # when being triggered too many calls in a short time frame.
55 # http://stackoverflow.com/questions/14698119/httpexception-deadline-exceeded-while-waiting-for-http-response-from-url-dead
56 http_auth = self._credentials.authorize(httplib2.Http(timeout=30))
57 if discovery_url is None:
58 self._service = apiclient.discovery.build(
59 self.service_name, self.service_version,
60 http=http_auth)
61 else:
62 self._service = apiclient.discovery.build(
63 self.service_name, self.service_version, http=http_auth,
64 discoveryServiceUrl=discovery_url)
xixuan878b1eb2017-03-20 15:58:17 -070065
Xixuan Wu5d6063e2017-09-05 16:15:07 -070066 def _init_credentials(self):
67 """Initialize the credentials for a google API."""
68 if (self.running_env == constants.RunningEnv.ENV_STANDALONE or
69 self.running_env == constants.RunningEnv.ENV_DEVELOPMENT_SERVER):
70 # Running locally
71 service_credentials = service_account.ServiceAccountCredentials
72 self._credentials = service_credentials.from_json_keyfile_name(
Xixuan Wu26d06e02017-09-20 14:50:28 -070073 file_getter.STAGING_CLIENT_SECRETS_FILE, self.scopes)
Xixuan Wu5d6063e2017-09-05 16:15:07 -070074 else:
75 # Running in app-engine production
76 self._credentials = appengine.AppAssertionCredentials(self.scopes)
xixuan878b1eb2017-03-20 15:58:17 -070077
78
79class AndroidBuildRestClient(object):
Xixuan Wu5d6063e2017-09-05 16:15:07 -070080 """REST client for android build API."""
xixuan878b1eb2017-03-20 15:58:17 -070081
Xixuan Wu5d6063e2017-09-05 16:15:07 -070082 def __init__(self, rest_client):
83 """Initialize a REST client for connecting to Android Build API."""
84 self._rest_client = rest_client
85 self._rest_client.create_service()
xixuan878b1eb2017-03-20 15:58:17 -070086
Xixuan Wu5d6063e2017-09-05 16:15:07 -070087 def get_latest_build_id(self, branch, target):
88 """Get the latest build id for a given branch and target.
xixuan878b1eb2017-03-20 15:58:17 -070089
Xixuan Wu5d6063e2017-09-05 16:15:07 -070090 Args:
91 branch: an android build's branch
92 target: an android build's target
xixuan878b1eb2017-03-20 15:58:17 -070093
Xixuan Wu5d6063e2017-09-05 16:15:07 -070094 Returns:
95 A string representing latest build id.
96 """
97 request = self._rest_client.service.build().list(
98 buildType='submitted',
99 branch=branch,
100 target=target,
101 successful=True,
102 maxResults=1)
103 builds = request.execute(num_retries=10)
104 if not builds or not builds['builds']:
105 return None
xixuan878b1eb2017-03-20 15:58:17 -0700106
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700107 return builds['builds'][0]['buildId']
xixuan878b1eb2017-03-20 15:58:17 -0700108
109
110class StorageRestClient(object):
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700111 """REST client for google storage API."""
xixuan878b1eb2017-03-20 15:58:17 -0700112
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700113 def __init__(self, rest_client):
114 """Initialize a REST client for connecting to Google storage API."""
115 self._rest_client = rest_client
116 self._rest_client.create_service()
xixuan878b1eb2017-03-20 15:58:17 -0700117
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700118 def read_object(self, input_bucket, input_object):
119 """Read the contents of input_object in input_bucket.
xixuan878b1eb2017-03-20 15:58:17 -0700120
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700121 Args:
122 input_bucket: the bucket for fetching.
123 input_object: the object for checking the contents.
xixuan878b1eb2017-03-20 15:58:17 -0700124
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700125 Returns:
126 the stripped string contents of the input object.
xixuan878b1eb2017-03-20 15:58:17 -0700127
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700128 Raises:
129 apiclient.errors.HttpError
130 """
131 req = self._rest_client.service.objects().get_media(
132 bucket=input_bucket,
133 object=input_object)
134 return req.execute()
xixuan878b1eb2017-03-20 15:58:17 -0700135
136
137class CalendarRestClient(object):
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700138 """Class of REST client for google calendar API."""
xixuan878b1eb2017-03-20 15:58:17 -0700139
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700140 def __init__(self, rest_client):
141 """Initialize a REST client for connecting to Google calendar API."""
142 self._rest_client = rest_client
143 self._rest_client.create_service()
xixuan878b1eb2017-03-20 15:58:17 -0700144
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700145 def add_event(self, calendar_id, input_event):
146 """Add events of a given calendar.
xixuan878b1eb2017-03-20 15:58:17 -0700147
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700148 Args:
149 calendar_id: the ID of the given calendar.
150 input_event: the event to be added.
151 """
152 self._rest_client.service.events().insert(
153 calendarId=calendar_id,
154 body=input_event).execute()
xixuan878b1eb2017-03-20 15:58:17 -0700155
156
157class SwarmingRestClient(object):
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700158 """REST client for swarming proxy API."""
xixuan878b1eb2017-03-20 15:58:17 -0700159
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700160 DISCOVERY_URL_PATTERN = '%s/discovery/v1/apis/%s/%s/rest'
xixuan878b1eb2017-03-20 15:58:17 -0700161
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700162 def __init__(self, rest_client, service_url):
163 self._rest_client = rest_client
164 discovery_url = self.DISCOVERY_URL_PATTERN % (
165 service_url, rest_client.service_name, rest_client.service_version)
166 self._rest_client.create_service(discovery_url=discovery_url)
xixuan878b1eb2017-03-20 15:58:17 -0700167
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700168 def create_task(self, request):
169 """Create new task.
xixuan878b1eb2017-03-20 15:58:17 -0700170
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700171 Args:
172 request: a json-compatible dict expected by swarming server.
173 See _to_raw_request's output in swarming_lib.py for details.
xixuan878b1eb2017-03-20 15:58:17 -0700174
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700175 Returns:
176 A json dict returned by API task.new.
177 """
178 return self._rest_client.service.tasks().new(
179 fields='request,task_id', body=request).execute()
xixuan878b1eb2017-03-20 15:58:17 -0700180
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700181 def get_task_result(self, task_id):
182 """Get task results by a given task_id.
xixuan878b1eb2017-03-20 15:58:17 -0700183
Xixuan Wu5d6063e2017-09-05 16:15:07 -0700184 Args:
185 task_id: A string, represents task id.
186
187 Returns:
188 A json dict returned by API task.result.
189 """
190 return self._rest_client.service.task().result(
191 task_id=task_id).execute()