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