blob: 640a43356b3ab10ebc58cbb988abb3ff603836d1 [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
12import settings
13
14from api import monorail_servicer
15from api import converters
16from api.api_proto import projects_pb2
17from api.api_proto import project_objects_pb2
18from api.api_proto import projects_prpc_pb2
19from businesslogic import work_env
20from framework import framework_bizobj
21from framework import exceptions
22from framework import framework_views
23from framework import permissions
24from project import project_helpers
25from tracker import tracker_bizobj
26from tracker import tracker_helpers
27
28# TODO(zhangtiff): Remove dependency on tracker_views.
29from tracker import tracker_views
30
31
32class ProjectsServicer(monorail_servicer.MonorailServicer):
33 """Handle API requests related to Project objects.
34
35 Each API request is implemented with a method as defined in the .proto
36 file that does any request-specific validation, uses work_env to
37 safely operate on business objects, and returns a response proto.
38 """
39
40 DESCRIPTION = projects_prpc_pb2.ProjectsServiceDescription
41
42 def _GetProject(self, mc, request, use_cache=True):
43 """Get the project object specified in the request."""
44 with work_env.WorkEnv(mc, self.services, phase='getting project') as we:
45 project = we.GetProjectByName(request.project_name, use_cache=use_cache)
46 # Perms in this project are already looked up in MonorailServicer.
47 return project
48
49 @monorail_servicer.PRPCMethod
50 def ListProjects(self, _mc, _request):
51 return projects_pb2.ListProjectsResponse(
52 projects=[
53 project_objects_pb2.Project(name='One'),
54 project_objects_pb2.Project(name='Two')],
55 next_page_token='next...')
56
57 @monorail_servicer.PRPCMethod
58 def ListProjectTemplates(self, mc, request):
59 """Return the specific project's templates."""
60 if not request.project_name:
61 raise exceptions.InputException('Param `project_name` required.')
62 project = self._GetProject(mc, request)
63
64 with work_env.WorkEnv(mc, self.services) as we:
65 config = we.GetProjectConfig(project.project_id)
66 templates = we.ListProjectTemplates(project.project_id)
67
68 with mc.profiler.Phase('converting to response objects'):
69 involved_user_ids = tracker_bizobj.UsersInvolvedInTemplates(templates)
70 users_by_id = framework_views.MakeAllUserViews(
71 mc.cnxn, self.services.user, involved_user_ids)
72 response = projects_pb2.ListProjectTemplatesResponse(
73 templates=converters.ConvertProjectTemplateDefs(
74 templates, users_by_id, config))
75
76 return response
77
78 @monorail_servicer.PRPCMethod
79 def GetConfig(self, mc, request):
80 """Return the specified project config."""
81 project = self._GetProject(mc, request)
82
83 with work_env.WorkEnv(mc, self.services) as we:
84 config = we.GetProjectConfig(project.project_id)
85
86 with mc.profiler.Phase('making user views'):
87 involved_user_ids = tracker_bizobj.UsersInvolvedInConfig(config)
88 users_by_id = framework_views.MakeAllUserViews(
89 mc.cnxn, self.services.user, involved_user_ids)
90 framework_views.RevealAllEmailsToMembers(
91 mc.cnxn, self.services, mc.auth, users_by_id, project)
92
93 label_ids = tracker_bizobj.LabelIDsInvolvedInConfig(config)
94 labels_by_id = {
95 label_id: self.services.config.LookupLabel(
96 mc.cnxn, config.project_id, label_id)
97 for label_id in label_ids}
98
99 result = converters.ConvertConfig(
100 project, config, users_by_id, labels_by_id)
101 return result
102
103 @monorail_servicer.PRPCMethod
104 def GetPresentationConfig(self, mc, request):
105 """Return the UI centric pieces of the project config."""
106 project = self._GetProject(mc, request)
107
108 with work_env.WorkEnv(mc, self.services) as we:
109 config = we.GetProjectConfig(project.project_id)
110
111 project_thumbnail_url = tracker_views.LogoView(project).thumbnail_url
112 project_summary = project.summary
113 custom_issue_entry_url = config.custom_issue_entry_url
114 revision_url_format = (
115 project.revision_url_format or settings.revision_url_format)
116
117 default_query = None
118 saved_queries = None
119
120 # Only show default query or project saved queries for project
121 # members, in case they contain sensitive information.
122 if framework_bizobj.UserIsInProject(
123 project, mc.auth.effective_ids):
124 default_query = config.member_default_query
125
126 saved_queries = self.services.features.GetCannedQueriesByProjectID(
127 mc.cnxn, project.project_id)
128
129 return project_objects_pb2.PresentationConfig(
130 project_thumbnail_url=project_thumbnail_url,
131 project_summary=project_summary,
132 custom_issue_entry_url=custom_issue_entry_url,
133 default_query=default_query,
134 default_col_spec=config.default_col_spec,
135 default_sort_spec=config.default_sort_spec,
136 default_x_attr=config.default_x_attr,
137 default_y_attr=config.default_y_attr,
138 saved_queries=converters.IngestSavedQueries(
139 mc.cnxn, self.services.project, saved_queries),
140 revision_url_format=revision_url_format)
141
142 @monorail_servicer.PRPCMethod
143 def GetCustomPermissions(self, mc, request):
144 """Return the custom permissions for the given project."""
145 project = self._GetProject(mc, request)
146 custom_permissions = permissions.GetCustomPermissions(project)
147
148 result = projects_pb2.GetCustomPermissionsResponse(
149 permissions=custom_permissions)
150 return result
151
152 @monorail_servicer.PRPCMethod
153 def GetVisibleMembers(self, mc, request):
154 """Return the members of the project that the user can see.
155
156 Raises:
157 PermissionException the user is not allowed to view the project members.
158 """
159 project = self._GetProject(mc, request)
160 if not permissions.CanViewContributorList(mc, project):
161 raise permissions.PermissionException(
162 'User is not allowed to view the project members')
163
164 users_by_id = tracker_helpers.GetVisibleMembers(mc, project, self.services)
165
166 sorted_user_ids = sorted(
167 users_by_id, key=lambda uid: users_by_id[uid].email)
168 user_refs = converters.ConvertUserRefs(
169 sorted_user_ids, [], users_by_id, True)
170 sorted_group_ids = sorted(
171 (uv.user_id for uv in users_by_id.values() if uv.is_group),
172 key=lambda uid: users_by_id[uid].email)
173 group_refs = converters.ConvertUserRefs(
174 sorted_group_ids, [], users_by_id, True)
175
176 result = projects_pb2.GetVisibleMembersResponse(
177 user_refs=user_refs, group_refs=group_refs)
178 return result
179
180 @monorail_servicer.PRPCMethod
181 def GetLabelOptions(self, mc, request):
182 """Return the label options for autocomplete for the given project."""
183 project = self._GetProject(mc, request)
184
185 with work_env.WorkEnv(mc, self.services) as we:
186 config = we.GetProjectConfig(project.project_id)
187
188 label_options = tracker_helpers.GetLabelOptions(
189 config, permissions.GetCustomPermissions(project))
190 label_defs = [
191 project_objects_pb2.LabelDef(
192 label=label['name'],
193 docstring=label['doc'])
194 for label in label_options]
195
196 result = projects_pb2.GetLabelOptionsResponse(
197 label_options=label_defs,
198 exclusive_label_prefixes=config.exclusive_label_prefixes)
199 return result
200
201 @monorail_servicer.PRPCMethod
202 def ListStatuses(self, mc, request):
203 """Return all well-known statuses in the specified project."""
204 project = self._GetProject(mc, request)
205
206 with work_env.WorkEnv(mc, self.services) as we:
207 config = we.GetProjectConfig(project.project_id)
208
209 status_defs = [
210 converters.ConvertStatusDef(sd)
211 for sd in config.well_known_statuses]
212 statuses_offer_merge = [
213 converters.ConvertStatusRef(sd.status, None, config)
214 for sd in config.well_known_statuses
215 if sd.status in config.statuses_offer_merge]
216
217 result = projects_pb2.ListStatusesResponse(
218 status_defs=status_defs,
219 statuses_offer_merge=statuses_offer_merge,
220 restrict_to_known=config.restrict_to_known)
221 return result
222
223 @monorail_servicer.PRPCMethod
224 def ListComponents(self, mc, request):
225 """Return all component defs in the specified project."""
226 project = self._GetProject(mc, request)
227
228 with work_env.WorkEnv(mc, self.services) as we:
229 config = we.GetProjectConfig(project.project_id)
230
231 with mc.profiler.Phase('making user views'):
232 users_by_id = {}
233 if request.include_admin_info:
234 users_involved = tracker_bizobj.UsersInvolvedInConfig(config)
235 users_by_id = framework_views.MakeAllUserViews(
236 mc.cnxn, self.services.user, users_involved)
237 framework_views.RevealAllEmailsToMembers(
238 mc.cnxn, self.services, mc.auth, users_by_id, project)
239
240 with mc.profiler.Phase('looking up labels'):
241 labels_by_id = {}
242 if request.include_admin_info:
243 label_ids = tracker_bizobj.LabelIDsInvolvedInConfig(config)
244 labels_by_id = {
245 label_id: self.services.config.LookupLabel(
246 mc.cnxn, config.project_id, label_id)
247 for label_id in label_ids}
248
249 component_defs = [
250 converters.ConvertComponentDef(
251 cd, users_by_id, labels_by_id, request.include_admin_info)
252 for cd in config.component_defs]
253
254 result = projects_pb2.ListComponentsResponse(
255 component_defs=component_defs)
256 return result
257
258 @monorail_servicer.PRPCMethod
259 def ListFields(self, mc, request):
260 """List all fields for the specified project."""
261 project = self._GetProject(mc, request)
262
263 with work_env.WorkEnv(mc, self.services) as we:
264 config = we.GetProjectConfig(project.project_id)
265
266 users_by_id = {}
267 users_for_perm = {}
268 # Only look for members if user choices are requested and there are user
269 # fields that need permissions.
270 if request.include_user_choices:
271 perms_needed = {
272 fd.needs_perm
273 for fd in config.field_defs
274 if fd.needs_perm and not fd.is_deleted}
275 if perms_needed:
276 users_by_id = tracker_helpers.GetVisibleMembers(
277 mc, project, self.services)
278 effective_ids_by_user = self.services.usergroup.LookupAllMemberships(
279 mc.cnxn, users_by_id)
280 users_for_perm = project_helpers.UsersWithPermsInProject(
281 project, perms_needed, users_by_id, effective_ids_by_user)
282
283 field_defs = [
284 converters.ConvertFieldDef(
285 fd, users_for_perm.get(fd.needs_perm, []), users_by_id, config,
286 request.include_admin_info)
287 for fd in config.field_defs
288 if not fd.is_deleted]
289
290 result = projects_pb2.ListFieldsResponse(field_defs=field_defs)
291 return result
292
293 @monorail_servicer.PRPCMethod
294 def GetProjectStarCount(self, mc, request):
295 """Get the star count for the specified project."""
296 project = self._GetProject(mc, request)
297
298 with work_env.WorkEnv(mc, self.services) as we:
299 star_count = we.GetProjectStarCount(project.project_id)
300
301 result = projects_pb2.GetProjectStarCountResponse(star_count=star_count)
302 return result
303
304 @monorail_servicer.PRPCMethod
305 def StarProject(self, mc, request):
306 """Star the specified project."""
307 project = self._GetProject(mc, request)
308
309 with work_env.WorkEnv(mc, self.services) as we:
310 we.StarProject(project.project_id, request.starred)
311 star_count = we.GetProjectStarCount(project.project_id)
312
313 result = projects_pb2.StarProjectResponse(star_count=star_count)
314 return result
315
316 @monorail_servicer.PRPCMethod
317 def CheckProjectName(self, mc, request):
318 """Check that a project name is valid and not already in use."""
319 with work_env.WorkEnv(mc, self.services) as we:
320 error = we.CheckProjectName(request.project_name)
321 result = projects_pb2.CheckProjectNameResponse(error=error)
322 return result
323
324 @monorail_servicer.PRPCMethod
325 def CheckComponentName(self, mc, request):
326 """Check that the component name is valid and not already in use."""
327 project = self._GetProject(mc, request)
328 with work_env.WorkEnv(mc, self.services) as we:
329 error = we.CheckComponentName(
330 project.project_id, request.parent_path, request.component_name)
331 result = projects_pb2.CheckComponentNameResponse(error=error)
332 return result
333
334 @monorail_servicer.PRPCMethod
335 def CheckFieldName(self, mc, request):
336 """Check that a field name is valid and not already in use."""
337 project = self._GetProject(mc, request)
338 with work_env.WorkEnv(mc, self.services) as we:
339 error = we.CheckFieldName(project.project_id, request.field_name)
340 result = projects_pb2.CheckFieldNameResponse(error=error)
341 return result