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"""Provides the web interface for filing a bug on the issue tracker."""
5from __future__ import print_function
6from __future__ import division
7from __future__ import absolute_import
8
9from google.appengine.api import users
10from google.appengine.ext import ndb
11
12from dashboard import oauth2_decorator
13from dashboard.common import file_bug
14from dashboard.common import request_handler
15from dashboard.common import utils
16
17
18class FileBugHandler(request_handler.RequestHandler):
19  """Uses oauth2 to file a new bug with a set of alerts."""
20
21  def post(self):
22    """A POST request for this endpoint is the same as a GET request."""
23    self.get()
24
25  @oauth2_decorator.DECORATOR.oauth_required
26  def get(self):
27    """Either shows the form to file a bug, or if filled in, files the bug.
28
29    The form to file a bug is popped up from the triage-dialog polymer element.
30    The default summary, description and label strings are constructed there.
31
32    Request parameters:
33      summary: Bug summary string.
34      description: Bug full description string.
35      keys: Comma-separated Alert keys in urlsafe format.
36      finish: Boolean set to true when creating a bug, false otherwise.
37      project_id: The Monorail project ID (used to create  a bug).
38      labels: Bug labels (used to create  a bug).
39      components: Bug components (used to create  a bug).
40      owner: Bug owner email address (used to create  a bug).
41      cc: Bug emails to CC (used to create  a bug).
42
43    Outputs:
44      HTML, using the template 'bug_result.html'.
45    """
46    if not utils.IsValidSheriffUser():
47      self.RenderHtml(
48          'bug_result.html', {
49              'error': 'You must be logged in with a chromium.org account '
50                       'to file bugs.'
51          })
52      return
53
54    summary = self.request.get('summary')
55    description = self.request.get('description')
56    keys = self.request.get('keys')
57
58    if not keys:
59      self.RenderHtml('bug_result.html',
60                      {'error': 'No alerts specified to add bugs to.'})
61      return
62
63    if self.request.get('finish'):
64      project_id = self.request.get('project_id', 'chromium')
65      labels = self.request.get_all('label')
66      components = self.request.get_all('component')
67      owner = self.request.get('owner')
68      cc = self.request.get('cc')
69      self._CreateBug(owner, cc, summary, description, project_id, labels,
70                      components, keys)
71    else:
72      self._ShowBugDialog(summary, description, keys)
73
74  def _ShowBugDialog(self, summary, description, urlsafe_keys):
75    """Sends a HTML page with a form for filing the bug.
76
77    Args:
78      summary: The default bug summary string.
79      description: The default bug description string.
80      urlsafe_keys: Comma-separated Alert keys in urlsafe format.
81    """
82    alert_keys = [ndb.Key(urlsafe=k) for k in urlsafe_keys.split(',')]
83    labels, components = file_bug.FetchLabelsAndComponents(alert_keys)
84    owner_components = file_bug.FetchBugComponents(alert_keys)
85    self.RenderHtml(
86        'bug_result.html', {
87            'bug_create_form': True,
88            'keys': urlsafe_keys,
89            'summary': summary,
90            'description': description,
91            'projects': utils.MONORAIL_PROJECTS,
92            'labels': labels,
93            'components': components.union(owner_components),
94            'owner': '',
95            'cc': users.get_current_user(),
96        })
97
98  def _CreateBug(self, owner, cc, summary, description, project_id, labels,
99                 components, urlsafe_keys):
100    """Creates a bug, associates it with the alerts, sends a HTML response.
101
102    Args:
103      owner: The owner of the bug, must end with @{project_id}.org or
104        @google.com if not empty.
105      cc: CSV of email addresses to CC on the bug.
106      summary: The new bug summary string.
107      description: The new bug description string.
108      project_id: The Monorail project ID used to create the bug.
109      labels: List of label strings for the new bug.
110      components: List of component strings for the new bug.
111      urlsafe_keys: Comma-separated alert keys in urlsafe format.
112    """
113    # Only project members (@{project_id}.org or @google.com accounts)
114    # can be owners of bugs.
115    project_domain = '@%s.org' % project_id
116    if owner and not owner.endswith(project_domain) and not owner.endswith(
117        '@google.com'):
118      self.RenderHtml(
119          'bug_result.html', {
120              'error':
121                  'Owner email address must end with %s or @google.com.' %
122                  project_domain
123          })
124      return
125
126    http = oauth2_decorator.DECORATOR.http()
127    template_params = file_bug.FileBug(http, owner, cc, summary, description,
128                                       project_id, labels, components,
129                                       urlsafe_keys.split(','))
130    self.RenderHtml('bug_result.html', template_params)
131