Chromium Code Reviews| 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 |