OLD | NEW |
---|---|
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import datetime | 5 import datetime |
6 import functools | 6 import functools |
7 import json | 7 import json |
8 import logging | 8 import logging |
9 | 9 |
10 from components import auth | 10 from components import auth |
(...skipping 25 matching lines...) Expand all Loading... | |
36 errors.InvalidBuildStateError: ErrorReason.INVALID_BUILD_STATE, | 36 errors.InvalidBuildStateError: ErrorReason.INVALID_BUILD_STATE, |
37 errors.BuildIsCompletedError: ErrorReason.BUILD_IS_COMPLETED, | 37 errors.BuildIsCompletedError: ErrorReason.BUILD_IS_COMPLETED, |
38 } | 38 } |
39 | 39 |
40 | 40 |
41 class ErrorMessage(messages.Message): | 41 class ErrorMessage(messages.Message): |
42 reason = messages.EnumField(ErrorReason, 1, required=True) | 42 reason = messages.EnumField(ErrorReason, 1, required=True) |
43 message = messages.StringField(2, required=True) | 43 message = messages.StringField(2, required=True) |
44 | 44 |
45 | 45 |
46 def exception_to_error_message(ex): | |
47 assert isinstance(ex, errors.Error) | |
48 return ErrorMessage( | |
49 reason=ERROR_REASON_MAP[type(ex)], | |
50 message=ex.message, | |
51 ) | |
52 | |
53 | |
54 class PutRequestMessage(messages.Message): | |
55 bucket = messages.StringField(1, required=True) | |
56 tags = messages.StringField(2, repeated=True) | |
57 parameters_json = messages.StringField(3) | |
58 lease_expiration_ts = messages.IntegerField(4) | |
59 | |
60 | |
46 class BuildMessage(messages.Message): | 61 class BuildMessage(messages.Message): |
47 """Describes model.Build, see its docstring.""" | 62 """Describes model.Build, see its docstring.""" |
48 id = messages.IntegerField(1, required=True) | 63 id = messages.IntegerField(1, required=True) |
49 bucket = messages.StringField(2, required=True) | 64 bucket = messages.StringField(2, required=True) |
50 tags = messages.StringField(3, repeated=True) | 65 tags = messages.StringField(3, repeated=True) |
51 parameters_json = messages.StringField(4) | 66 parameters_json = messages.StringField(4) |
52 status = messages.EnumField(model.BuildStatus, 5) | 67 status = messages.EnumField(model.BuildStatus, 5) |
53 result = messages.EnumField(model.BuildResult, 6) | 68 result = messages.EnumField(model.BuildResult, 6) |
54 result_details_json = messages.StringField(7) | 69 result_details_json = messages.StringField(7) |
55 failure_reason = messages.EnumField(model.FailureReason, 8) | 70 failure_reason = messages.EnumField(model.FailureReason, 8) |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
149 endpoints_decorator = auth.endpoints_method( | 164 endpoints_decorator = auth.endpoints_method( |
150 request_message_class, response_message_class, **kwargs) | 165 request_message_class, response_message_class, **kwargs) |
151 | 166 |
152 def decorator(fn): | 167 def decorator(fn): |
153 @functools.wraps(fn) | 168 @functools.wraps(fn) |
154 def decorated(*args, **kwargs): | 169 def decorated(*args, **kwargs): |
155 try: | 170 try: |
156 return fn(*args, **kwargs) | 171 return fn(*args, **kwargs) |
157 except errors.Error as ex: | 172 except errors.Error as ex: |
158 assert hasattr(response_message_class, 'error') | 173 assert hasattr(response_message_class, 'error') |
159 return response_message_class(error=ErrorMessage( | 174 return response_message_class(error=exception_to_error_message(ex)) |
160 reason=ERROR_REASON_MAP[type(ex)], | |
161 message=ex.message, | |
162 )) | |
163 return endpoints_decorator(decorated) | 175 return endpoints_decorator(decorated) |
164 return decorator | 176 return decorator |
165 | 177 |
166 | 178 |
167 def parse_json(json_data, param_name): | 179 def parse_json(json_data, param_name): |
168 if not json_data: | 180 if not json_data: |
169 return None | 181 return None |
170 try: | 182 try: |
171 return json.loads(json_data) | 183 return json.loads(json_data) |
172 except ValueError as ex: | 184 except ValueError as ex: |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
211 path='builds/{id}', http_method='GET') | 223 path='builds/{id}', http_method='GET') |
212 def get(self, request): | 224 def get(self, request): |
213 """Returns a build by id.""" | 225 """Returns a build by id.""" |
214 build = self.service.get(request.id) | 226 build = self.service.get(request.id) |
215 if build is None: | 227 if build is None: |
216 raise errors.BuildNotFoundError() | 228 raise errors.BuildNotFoundError() |
217 return build_to_response_message(build) | 229 return build_to_response_message(build) |
218 | 230 |
219 ################################### PUT #################################### | 231 ################################### PUT #################################### |
220 | 232 |
221 class PutRequestMessage(messages.Message): | |
222 bucket = messages.StringField(1, required=True) | |
223 tags = messages.StringField(2, repeated=True) | |
224 parameters_json = messages.StringField(3) | |
225 lease_expiration_ts = messages.IntegerField(4) | |
226 | |
227 @buildbucket_api_method( | 233 @buildbucket_api_method( |
228 PutRequestMessage, BuildResponseMessage, | 234 PutRequestMessage, BuildResponseMessage, |
229 path='builds', http_method='PUT') | 235 path='builds', http_method='PUT') |
230 def put(self, request): | 236 def put(self, request): |
231 """Creates a new build.""" | 237 """Creates a new build.""" |
232 if not request.bucket: | |
233 raise errors.InvalidInputError('Bucket not specified') | |
234 | |
235 build = self.service.add( | 238 build = self.service.add( |
236 bucket=request.bucket, | 239 bucket=request.bucket, |
237 tags=request.tags, | 240 tags=request.tags, |
238 parameters=parse_json(request.parameters_json, 'parameters_json'), | 241 parameters=parse_json(request.parameters_json, 'parameters_json'), |
239 lease_expiration_date=parse_datetime(request.lease_expiration_ts), | 242 lease_expiration_date=parse_datetime(request.lease_expiration_ts), |
240 ) | 243 ) |
241 return build_to_response_message(build, include_lease_key=True) | 244 return build_to_response_message(build, include_lease_key=True) |
242 | 245 |
246 ################################ PUT_BATCH ################################# | |
247 | |
248 class PutBatchRequestMessage(messages.Message): | |
249 builds = messages.MessageField(PutRequestMessage, 1, repeated=True) | |
250 | |
251 class PutBatchResponseMessage(messages.Message): | |
252 builds = messages.MessageField(BuildMessage, 1, repeated=True) | |
253 error = messages.MessageField(ErrorMessage, 2) | |
254 | |
255 @buildbucket_api_method( | |
256 PutBatchRequestMessage, PutBatchResponseMessage, | |
257 path='builds/batch', http_method='PUT') | |
258 def put_batch(self, request): | |
Vadim Sh.
2015/04/14 18:56:40
btw, apiary APIs exposed by Google support batchin
nodir
2015/04/14 22:13:05
It cannot
| |
259 """Creates up to 25 new builds transactionally.""" | |
260 builds = self.service.add_batch([ | |
261 { | |
262 'bucket': build_req.bucket, | |
263 'tags': build_req.tags, | |
264 'parameters': parse_json( | |
265 build_req.parameters_json, 'parameters_json'), | |
266 'lease_expiration_date': parse_datetime( | |
267 build_req.lease_expiration_ts), | |
268 } | |
269 for build_req in request.builds | |
270 ]) | |
271 return self.PutBatchResponseMessage( | |
272 builds=[build_to_message(b, include_lease_key=True) for b in builds], | |
273 ) | |
274 | |
275 | |
243 ################################## SEARCH ################################# | 276 ################################## SEARCH ################################# |
244 | 277 |
245 SEARCH_REQUEST_RESOURCE_CONTAINER = endpoints.ResourceContainer( | 278 SEARCH_REQUEST_RESOURCE_CONTAINER = endpoints.ResourceContainer( |
246 message_types.VoidMessage, | 279 message_types.VoidMessage, |
247 start_cursor=messages.StringField(1), | 280 start_cursor=messages.StringField(1), |
248 # All specified tags must be present in a build. | 281 # All specified tags must be present in a build. |
249 bucket=messages.StringField(2, repeated=True), | 282 bucket=messages.StringField(2, repeated=True), |
250 tag=messages.StringField(3, repeated=True), | 283 tag=messages.StringField(3, repeated=True), |
251 status=messages.EnumField(model.BuildStatus, 4), | 284 status=messages.EnumField(model.BuildStatus, 4), |
252 result=messages.EnumField(model.BuildResult, 5), | 285 result=messages.EnumField(model.BuildResult, 5), |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
512 rules=[ | 545 rules=[ |
513 acl.Rule( | 546 acl.Rule( |
514 role=rule.role, | 547 role=rule.role, |
515 group=rule.group, | 548 group=rule.group, |
516 ) | 549 ) |
517 for rule in request.rules | 550 for rule in request.rules |
518 ], | 551 ], |
519 ) | 552 ) |
520 bucket_acl = acl.set_acl(request.bucket, bucket_acl) | 553 bucket_acl = acl.set_acl(request.bucket, bucket_acl) |
521 return bucket_acl_to_response_message(bucket_acl) | 554 return bucket_acl_to_response_message(bucket_acl) |
OLD | NEW |