blob: 2ae49c5ec029aca32925f9e22b96b922197719b5 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2018 The Chromium Authors. All rights reserved.
2# Use of this source code is governed 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
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
10import logging
11
12from api import monorail_servicer
13from api import converters
14from api.api_proto import common_pb2
15from api.api_proto import features_pb2
16from api.api_proto import features_prpc_pb2
17from businesslogic import work_env
18from features import component_helpers
19from features import features_bizobj
20from framework import exceptions
21from framework import framework_bizobj
22from framework import framework_views
23from framework import paginate
24from framework import permissions
25from services import features_svc
26from tracker import tracker_bizobj
27
28
29class FeaturesServicer(monorail_servicer.MonorailServicer):
30 """Handle API requests related to Features objects.
31
32 Each API request is implemented with a method as defined in the .proto
33 file that does any request-specific validation, uses work_env to
34 safely operate on business objects, and returns a response proto.
35 """
36
37 DESCRIPTION = features_prpc_pb2.FeaturesServiceDescription
38
39 @monorail_servicer.PRPCMethod
40 def ListHotlistsByUser(self, mc, request):
41 """Return the hotlists for the given user."""
42 user_id = converters.IngestUserRef(
43 mc.cnxn, request.user, self.services.user)
44
45 with work_env.WorkEnv(mc, self.services) as we:
46 mc.LookupLoggedInUserPerms(None)
47 hotlists = we.ListHotlistsByUser(user_id)
48
49 with mc.profiler.Phase('making user views'):
50 users_involved = features_bizobj.UsersInvolvedInHotlists(hotlists)
51 users_by_id = framework_views.MakeAllUserViews(
52 mc.cnxn, self.services.user, users_involved)
53 framework_views.RevealAllEmailsToMembers(
54 mc.cnxn, self.services, mc.auth, users_by_id)
55
56 converted_hotlists = [
57 converters.ConvertHotlist(hotlist, users_by_id)
58 for hotlist in hotlists]
59
60 result = features_pb2.ListHotlistsByUserResponse(
61 hotlists=converted_hotlists)
62
63 return result
64
65 @monorail_servicer.PRPCMethod
66 def ListHotlistsByIssue(self, mc, request):
67 # type: (MonorailConnection, proto.features.ListHotlistsByIssueRequest) ->
68 # proto.features.ListHotlistsByIssueResponse
69 """Return the hotlists the given issue is part of."""
70 issue_id = converters.IngestIssueRefs(
71 mc.cnxn, [request.issue], self.services)[0]
72
73 with work_env.WorkEnv(mc, self.services) as we:
74 project = we.GetProjectByName(request.issue.project_name)
75 mc.LookupLoggedInUserPerms(project)
76 hotlists = we.ListHotlistsByIssue(issue_id)
77
78 # Reduce spam by only showing hotlists that belong to a project member.
79 project_hotlists = [
80 hotlist for hotlist in hotlists if framework_bizobj.UserIsInProject(
81 project, set(hotlist.owner_ids + hotlist.editor_ids)) or
82 features_bizobj.UserIsInHotlist(hotlist, mc.auth.effective_ids)
83 ]
84 if project_hotlists and len(hotlists) / len(project_hotlists) > 10:
85 logging.warning(
86 'Unusual hotlist activity in %s:%s' %
87 (request.issue.project_name, issue_id))
88
89 with mc.profiler.Phase('making user views'):
90 users_involved = features_bizobj.UsersInvolvedInHotlists(project_hotlists)
91 users_by_id = framework_views.MakeAllUserViews(
92 mc.cnxn, self.services.user, users_involved)
93 framework_views.RevealAllEmailsToMembers(
94 mc.cnxn, self.services, mc.auth, users_by_id)
95
96 converted_hotlists = [
97 converters.ConvertHotlist(hotlist, users_by_id)
98 for hotlist in project_hotlists
99 ]
100
101 result = features_pb2.ListHotlistsByIssueResponse(
102 hotlists=converted_hotlists)
103
104 return result
105
106 @monorail_servicer.PRPCMethod
107 def ListRecentlyVisitedHotlists(self, mc, _request):
108 """Return the recently visited hotlists for the logged in user."""
109 with work_env.WorkEnv(mc, self.services) as we:
110 mc.LookupLoggedInUserPerms(None)
111 hotlists = we.ListRecentlyVisitedHotlists()
112
113 with mc.profiler.Phase('making user views'):
114 users_involved = features_bizobj.UsersInvolvedInHotlists(hotlists)
115 users_by_id = framework_views.MakeAllUserViews(
116 mc.cnxn, self.services.user, users_involved)
117 framework_views.RevealAllEmailsToMembers(
118 mc.cnxn, self.services, mc.auth, users_by_id)
119
120 converted_hotlists = [
121 converters.ConvertHotlist(hotlist, users_by_id)
122 for hotlist in hotlists]
123
124 result = features_pb2.ListRecentlyVisitedHotlistsResponse(
125 hotlists=converted_hotlists)
126
127 return result
128
129 @monorail_servicer.PRPCMethod
130 def ListStarredHotlists(self, mc, _request):
131 """Return the starred hotlists for the logged in user."""
132 with work_env.WorkEnv(mc, self.services) as we:
133 mc.LookupLoggedInUserPerms(None)
134 hotlists = we.ListStarredHotlists()
135
136 with mc.profiler.Phase('maknig user views'):
137 users_involved = features_bizobj.UsersInvolvedInHotlists(hotlists)
138 users_by_id = framework_views.MakeAllUserViews(
139 mc.cnxn, self.services.user, users_involved)
140 framework_views.RevealAllEmailsToMembers(
141 mc.cnxn, self.services, mc.auth, users_by_id)
142
143 converted_hotlists = [
144 converters.ConvertHotlist(hotlist, users_by_id)
145 for hotlist in hotlists]
146
147 result = features_pb2.ListStarredHotlistsResponse(
148 hotlists=converted_hotlists)
149
150 return result
151
152 @monorail_servicer.PRPCMethod
153 def GetHotlistStarCount(self, mc, request):
154 """Get the star count for the specified hotlist."""
155 hotlist_id = converters.IngestHotlistRef(
156 mc.cnxn, self.services.user, self.services.features,
157 request.hotlist_ref)
158
159 with work_env.WorkEnv(mc, self.services) as we:
160 mc.LookupLoggedInUserPerms(None)
161 star_count = we.GetHotlistStarCount(hotlist_id)
162
163 result = features_pb2.GetHotlistStarCountResponse(star_count=star_count)
164 return result
165
166 @monorail_servicer.PRPCMethod
167 def StarHotlist(self, mc, request):
168 """Star the specified hotlist."""
169 hotlist_id = converters.IngestHotlistRef(
170 mc.cnxn, self.services.user, self.services.features,
171 request.hotlist_ref)
172
173 with work_env.WorkEnv(mc, self.services) as we:
174 mc.LookupLoggedInUserPerms(None)
175 we.StarHotlist(hotlist_id, request.starred)
176 star_count = we.GetHotlistStarCount(hotlist_id)
177
178 result = features_pb2.StarHotlistResponse(star_count=star_count)
179 return result
180
181 @monorail_servicer.PRPCMethod
182 def GetHotlist(self, mc, request):
183 """Get the Hotlist metadata for the specified hotlist."""
184 hotlist_id = converters.IngestHotlistRef(
185 mc.cnxn, self.services.user, self.services.features,
186 request.hotlist_ref)
187
188 with work_env.WorkEnv(mc, self.services) as we:
189 mc.LookupLoggedInUserPerms(None)
190 hotlist = we.GetHotlist(hotlist_id)
191
192 with mc.profiler.Phase('making user views'):
193 users_involved = features_bizobj.UsersInvolvedInHotlists([hotlist])
194 users_by_id = framework_views.MakeAllUserViews(
195 mc.cnxn, self.services.user, users_involved)
196 framework_views.RevealAllEmailsToMembers(
197 mc.cnxn, self.services, mc.auth, users_by_id)
198
199 converted_hotlist = converters.ConvertHotlist(hotlist, users_by_id)
200 return features_pb2.GetHotlistResponse(hotlist=converted_hotlist)
201
202 @monorail_servicer.PRPCMethod
203 def CreateHotlist(self, mc, request):
204 """Create a new hotlist."""
205 editor_ids = converters.IngestUserRefs(
206 mc.cnxn, request.editor_refs, self.services.user)
207 issue_ids = converters.IngestIssueRefs(
208 mc.cnxn, request.issue_refs, self.services)
209
210 with work_env.WorkEnv(mc, self.services) as we:
211 we.CreateHotlist(
212 request.name, request.summary, request.description, editor_ids,
213 issue_ids, request.is_private, '')
214
215 result = features_pb2.CreateHotlistResponse()
216 return result
217
218 @monorail_servicer.PRPCMethod
219 def CheckHotlistName(self, mc, request):
220 """Check that a hotlist name is valid and not already in use."""
221 with work_env.WorkEnv(mc, self.services) as we:
222 error = we.CheckHotlistName(request.name)
223 result = features_pb2.CheckHotlistNameResponse(error=error)
224 return result
225
226 @monorail_servicer.PRPCMethod
227 def RemoveIssuesFromHotlists(self, mc, request):
228 """Remove the given issues from the given hotlists."""
229 hotlist_ids = converters.IngestHotlistRefs(
230 mc.cnxn, self.services.user, self.services.features,
231 request.hotlist_refs)
232 issue_ids = converters.IngestIssueRefs(
233 mc.cnxn, request.issue_refs, self.services)
234
235 with work_env.WorkEnv(mc, self.services) as we:
236 mc.LookupLoggedInUserPerms(None)
237 we.RemoveIssuesFromHotlists(hotlist_ids, issue_ids)
238
239 result = features_pb2.RemoveIssuesFromHotlistsResponse()
240 return result
241
242 @monorail_servicer.PRPCMethod
243 def AddIssuesToHotlists(self, mc, request):
244 """Add the given issues to the given hotlists."""
245 hotlist_ids = converters.IngestHotlistRefs(
246 mc.cnxn, self.services.user, self.services.features,
247 request.hotlist_refs)
248 issue_ids = converters.IngestIssueRefs(
249 mc.cnxn, request.issue_refs, self.services)
250
251 with work_env.WorkEnv(mc, self.services) as we:
252 mc.LookupLoggedInUserPerms(None)
253 we.AddIssuesToHotlists(hotlist_ids, issue_ids, request.note)
254
255 result = features_pb2.AddIssuesToHotlistsResponse()
256 return result
257
258 @monorail_servicer.PRPCMethod
259 def RerankHotlistIssues(self, mc, request):
260 """Rerank issues in the given hotlist."""
261 hotlist_id = converters.IngestHotlistRef(
262 mc.cnxn, self.services.user, self.services.features,
263 request.hotlist_ref)
264 moved_issue_ids = converters.IngestIssueRefs(
265 mc.cnxn, request.moved_refs, self.services)
266 [target_issue_id] = converters.IngestIssueRefs(
267 mc.cnxn, [request.target_ref], self.services)
268
269 with work_env.WorkEnv(mc, self.services) as we:
270 we.RerankHotlistIssues(
271 hotlist_id, moved_issue_ids, target_issue_id, request.split_above)
272
273 # TODO(jojwang): return updated hotlist items.
274 with mc.profiler.Phase('converting to response objects'):
275 result = features_pb2.RerankHotlistIssuesResponse()
276
277 return result
278
279 @monorail_servicer.PRPCMethod
280 def UpdateHotlistIssueNote(self, mc, request):
281 """Update the note for the given issue in the given hotlist."""
282 hotlist_id = converters.IngestHotlistRef(
283 mc.cnxn, self.services.user, self.services.features,
284 request.hotlist_ref)
285 issue_id = converters.IngestIssueRefs(
286 mc.cnxn, [request.issue_ref], self.services)[0]
287
288 with work_env.WorkEnv(mc, self.services) as we:
289 project = we.GetProjectByName(request.issue_ref.project_name)
290 mc.LookupLoggedInUserPerms(project)
291 we.UpdateHotlistIssueNote(hotlist_id, issue_id, request.note)
292
293 result = features_pb2.UpdateHotlistIssueNoteResponse()
294 return result
295
296 @monorail_servicer.PRPCMethod
297 def DeleteHotlist(self, mc, request):
298 """Delete the given hotlist"""
299 hotlist_id = converters.IngestHotlistRef(
300 mc.cnxn, self.services.user, self.services.features,
301 request.hotlist_ref)
302
303 with work_env.WorkEnv(mc, self.services) as we:
304 we.DeleteHotlist(hotlist_id)
305
306 return features_pb2.DeleteHotlistResponse()
307
308 # TODO(https://crbug.com/monorail/7515): Replace or delete PredictComponent.
309 @monorail_servicer.PRPCMethod
310 def PredictComponent(self, mc, request):
311 """Predict the component of an issue based on the given text."""
312 with work_env.WorkEnv(mc, self.services) as we:
313 project = we.GetProjectByName(request.project_name)
314 config = we.GetProjectConfig(project.project_id)
315
316 component_ref = None
317 component_id = component_helpers.PredictComponent(request.text, config)
318
319 if component_id:
320 component_ref = converters.ConvertComponentRef(component_id, config)
321
322 result = features_pb2.PredictComponentResponse(component_ref=component_ref)
323 return result