Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(267)

Side by Side Diff: appengine/monorail/features/commitlogcommands.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/monorail/features/commands.py ('k') | appengine/monorail/features/cues.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is govered by a BSD-style
3 # license that can be found in the LICENSE file or at
4 # https://developers.google.com/open-source/licenses/bsd
5
6 """Implements processing of issue update command lines.
7
8 This currently processes the leading command-lines that appear
9 at the top of inbound email messages to update existing issues.
10
11 It could also be expanded to allow new issues to be created. Or, to
12 handle commands in commit-log messages if the version control system
13 invokes a webhook.
14 """
15
16 import logging
17 import re
18
19 from features import commands
20 from features import notify
21 from framework import emailfmt
22 from framework import framework_bizobj
23 from framework import framework_helpers
24 from services import issue_svc
25
26
27 # Actions have separate 'Parse' and 'Run' implementations to allow better
28 # testing coverage.
29 class IssueAction(object):
30 """Base class for all issue commands."""
31
32 def __init__(self):
33 self.parser = commands.AssignmentParser(None)
34 self.description = ''
35 self.inbound_message = None
36 self.commenter_id = None
37 self.project = None
38 self.config = None
39 self.hostport = framework_helpers.GetHostPort()
40
41 def Parse(
42 self, cnxn, project_name, commenter_id, lines, services,
43 strip_quoted_lines=False, hostport=None):
44 """Populate object from raw user input."""
45 self.project = services.project.GetProjectByName(cnxn, project_name)
46 self.config = services.config.GetProjectConfig(
47 cnxn, self.project.project_id)
48 self.commenter_id = commenter_id
49
50 # Process all valid key-value lines. Once we find a non key-value line,
51 # treat the rest as the 'description'.
52 for idx, line in enumerate(lines):
53 valid_line = False
54 m = re.match(r'^\s*(\w+)\s*\:\s*(.*?)\s*$', line)
55 if m:
56 # Process Key-Value
57 key = m.group(1).lower()
58 value = m.group(2)
59 valid_line = self.parser.ParseAssignment(
60 cnxn, key, value, self.config, services, self.commenter_id)
61
62 if not valid_line:
63 # Not Key-Value. Treat this line and remaining as 'description'.
64 # First strip off any trailing blank lines.
65 while lines and not lines[-1].strip():
66 lines.pop()
67 if lines:
68 self.description = '\n'.join(lines[idx:])
69 break
70
71 if strip_quoted_lines:
72 self.inbound_message = '\n'.join(lines)
73 self.description = emailfmt.StripQuotedText(self.description)
74
75 if hostport:
76 self.hostport = hostport
77
78 for key in ['owner_id', 'cc_add', 'cc_remove', 'summary',
79 'status', 'labels_add', 'labels_remove', 'branch']:
80 logging.info('\t%s: %s', key, self.parser.__dict__[key])
81
82 for key in ['commenter_id', 'description', 'hostport']:
83 logging.info('\t%s: %s', key, self.__dict__[key])
84
85 def Run(self, cnxn, services, allow_edit=True):
86 """Execute this action."""
87 raise NotImplementedError()
88
89
90 class UpdateIssueAction(IssueAction):
91 """Implements processing email replies or the "update issue" command."""
92
93 def __init__(self, local_id):
94 super(UpdateIssueAction, self).__init__()
95 self.local_id = local_id
96
97 def Run(self, cnxn, services, allow_edit=True):
98 """Updates an issue based on the parsed commands."""
99 try:
100 issue = services.issue.GetIssueByLocalID(
101 cnxn, self.project.project_id, self.local_id)
102 except issue_svc.NoSuchIssueException:
103 return # Issue does not exist, so do nothing
104
105 old_owner_id = issue.owner_id
106 new_summary = self.parser.summary or issue.summary
107
108 if self.parser.status is None:
109 new_status = issue.status
110 else:
111 new_status = self.parser.status
112
113 if self.parser.owner_id is None:
114 new_owner_id = issue.owner_id
115 else:
116 new_owner_id = self.parser.owner_id
117
118 new_cc_ids = [cc for cc in list(issue.cc_ids) + list(self.parser.cc_add)
119 if cc not in self.parser.cc_remove]
120 (new_labels, _update_add,
121 _update_remove) = framework_bizobj.MergeLabels(
122 issue.labels, self.parser.labels_add,
123 self.parser.labels_remove,
124 self.config.exclusive_label_prefixes)
125
126 new_field_values = issue.field_values # TODO(jrobbins): edit custom ones
127
128 if not allow_edit:
129 # If user can't edit, then only consider the plain-text comment,
130 # and set all other fields back to their original values.
131 logging.info('Processed reply from user who can not edit issue')
132 new_summary = issue.summary
133 new_status = issue.status
134 new_owner_id = issue.owner_id
135 new_cc_ids = issue.cc_ids
136 new_labels = issue.labels
137 new_field_values = issue.field_values
138
139 amendments, _comment_pb = services.issue.ApplyIssueComment(
140 cnxn, services, self.commenter_id,
141 self.project.project_id, issue.local_id, new_summary, new_status,
142 new_owner_id, new_cc_ids, new_labels, new_field_values,
143 issue.component_ids, issue.blocked_on_iids, issue.blocking_iids,
144 issue.dangling_blocked_on_refs, issue.dangling_blocking_refs,
145 issue.merged_into, comment=self.description,
146 inbound_message=self.inbound_message)
147
148 logging.info('Updated issue %s:%s w/ amendments %r',
149 self.project.project_name, issue.local_id, amendments)
150
151 if amendments or self.description: # Avoid completely empty comments.
152 cmnts = services.issue.GetCommentsForIssue(cnxn, issue.issue_id)
153 notify.PrepareAndSendIssueChangeNotification(
154 self.project.project_id, self.local_id, self.hostport,
155 self.commenter_id, len(cmnts) - 1, old_owner_id=old_owner_id)
OLDNEW
« no previous file with comments | « appengine/monorail/features/commands.py ('k') | appengine/monorail/features/cues.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698