1# Copyright 2015 The Chromium 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"""URI endpoint for nudging Anomaly entities and updating alert bug IDs.""" 6 7import json 8 9from google.appengine.api import users 10from google.appengine.ext import ndb 11 12from dashboard import request_handler 13from dashboard import utils 14from dashboard import xsrf 15 16 17class EditAnomaliesHandler(request_handler.RequestHandler): 18 """Handles editing the bug IDs and revision range of Alerts.""" 19 20 @xsrf.TokenRequired 21 def post(self): 22 """Allows adding or resetting bug IDs and invalid statuses to Alerts. 23 24 Additionally, this endpoint is also responsible for changing the start 25 and end revisions of Anomaly entities. 26 27 Request parameters: 28 keys: A comma-separated list of urlsafe keys of Anomaly entities. 29 bug_id: The new bug ID. This should be either the string REMOVE 30 (indicating resetting the bug ID to None), or an integer. A negative 31 integer indicates an invalid or ignored alert. If this is given, then 32 the start and end revision ranges are ignored. 33 new_start_revision: New start revision value for the alert. 34 new_end_revision: New end revision value for the alert. 35 36 Outputs: 37 JSON which indicates the result. If an error has occurred, the field 38 "error" should be in the result. If successful, the response is still 39 expected to be JSON. 40 """ 41 if not utils.IsValidSheriffUser(): 42 user = users.get_current_user() 43 self.ReportError('User "%s" not authorized.' % user, status=403) 44 return 45 46 # Get the list of alerts to modify. 47 urlsafe_keys = self.request.get('keys') 48 if not urlsafe_keys: 49 self.response.out.write(json.dumps({ 50 'error': 'No alerts specified to add bugs to.'})) 51 return 52 keys = [ndb.Key(urlsafe=k) for k in urlsafe_keys.split(',')] 53 alert_entities = ndb.get_multi(keys) 54 55 # Get the parameters which specify the changes to make. 56 bug_id = self.request.get('bug_id') 57 new_start_revision = self.request.get('new_start_revision') 58 new_end_revision = self.request.get('new_end_revision') 59 if bug_id: 60 self.ChangeBugId(alert_entities, bug_id) 61 elif new_start_revision and new_end_revision: 62 self.NudgeAnomalies(alert_entities, new_start_revision, new_end_revision) 63 else: 64 self.response.out.write( 65 json.dumps({'error': 'No bug ID or new revision specified.'})) 66 67 def ChangeBugId(self, alert_entities, bug_id): 68 """Changes or resets the bug ID of all given alerts.""" 69 # Change the bug ID if a new bug ID is specified and valid. 70 if bug_id == 'REMOVE': 71 bug_id = None 72 else: 73 try: 74 bug_id = int(bug_id) 75 except ValueError: 76 self.response.out.write(json.dumps({ 77 'error': 'Invalid bug ID %s' % str(bug_id)})) 78 return 79 for a in alert_entities: 80 a.bug_id = bug_id 81 82 ndb.put_multi(alert_entities) 83 self.response.out.write(json.dumps({'bug_id': bug_id})) 84 85 def NudgeAnomalies(self, anomaly_entities, start, end): 86 # Change the revision range if a new revision range is specified and valid. 87 try: 88 start = int(start) 89 end = int(end) 90 except ValueError: 91 self.response.out.write( 92 json.dumps({'error': 'Invalid revisions %s, %s' % (start, end)})) 93 return 94 for a in anomaly_entities: 95 a.start_revision = start 96 a.end_revision = end 97 98 ndb.put_multi(anomaly_entities) 99 self.response.out.write(json.dumps({'success': 'Alerts nudged.'})) 100