| # Copyright 2018 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import print_function |
| from __future__ import division |
| from __future__ import absolute_import |
| |
| import logging |
| |
| import settings |
| |
| from api import monorail_servicer |
| from api import converters |
| from api.api_proto import projects_pb2 |
| from api.api_proto import project_objects_pb2 |
| from api.api_proto import projects_prpc_pb2 |
| from businesslogic import work_env |
| from framework import framework_bizobj |
| from framework import exceptions |
| from framework import framework_views |
| from framework import permissions |
| from project import project_helpers |
| from tracker import tracker_bizobj |
| from tracker import tracker_helpers |
| |
| # TODO(zhangtiff): Remove dependency on tracker_views. |
| from tracker import tracker_views |
| |
| |
| class ProjectsServicer(monorail_servicer.MonorailServicer): |
| """Handle API requests related to Project objects. |
| |
| Each API request is implemented with a method as defined in the .proto |
| file that does any request-specific validation, uses work_env to |
| safely operate on business objects, and returns a response proto. |
| """ |
| |
| DESCRIPTION = projects_prpc_pb2.ProjectsServiceDescription |
| |
| def _GetProject(self, mc, request, use_cache=True): |
| """Get the project object specified in the request.""" |
| with work_env.WorkEnv(mc, self.services, phase='getting project') as we: |
| project = we.GetProjectByName(request.project_name, use_cache=use_cache) |
| # Perms in this project are already looked up in MonorailServicer. |
| return project |
| |
| @monorail_servicer.PRPCMethod |
| def ListProjects(self, _mc, _request): |
| return projects_pb2.ListProjectsResponse( |
| projects=[ |
| project_objects_pb2.Project(name='One'), |
| project_objects_pb2.Project(name='Two')], |
| next_page_token='next...') |
| |
| @monorail_servicer.PRPCMethod |
| def ListProjectTemplates(self, mc, request): |
| """Return the specific project's templates.""" |
| if not request.project_name: |
| raise exceptions.InputException('Param `project_name` required.') |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| config = we.GetProjectConfig(project.project_id) |
| templates = we.ListProjectTemplates(project.project_id) |
| |
| with mc.profiler.Phase('converting to response objects'): |
| involved_user_ids = tracker_bizobj.UsersInvolvedInTemplates(templates) |
| users_by_id = framework_views.MakeAllUserViews( |
| mc.cnxn, self.services.user, involved_user_ids) |
| response = projects_pb2.ListProjectTemplatesResponse( |
| templates=converters.ConvertProjectTemplateDefs( |
| templates, users_by_id, config)) |
| |
| return response |
| |
| @monorail_servicer.PRPCMethod |
| def GetConfig(self, mc, request): |
| """Return the specified project config.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| config = we.GetProjectConfig(project.project_id) |
| |
| with mc.profiler.Phase('making user views'): |
| involved_user_ids = tracker_bizobj.UsersInvolvedInConfig(config) |
| users_by_id = framework_views.MakeAllUserViews( |
| mc.cnxn, self.services.user, involved_user_ids) |
| framework_views.RevealAllEmailsToMembers( |
| mc.cnxn, self.services, mc.auth, users_by_id, project) |
| |
| label_ids = tracker_bizobj.LabelIDsInvolvedInConfig(config) |
| labels_by_id = { |
| label_id: self.services.config.LookupLabel( |
| mc.cnxn, config.project_id, label_id) |
| for label_id in label_ids} |
| |
| result = converters.ConvertConfig( |
| project, config, users_by_id, labels_by_id) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def GetPresentationConfig(self, mc, request): |
| """Return the UI centric pieces of the project config.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| config = we.GetProjectConfig(project.project_id) |
| |
| project_thumbnail_url = tracker_views.LogoView(project).thumbnail_url |
| project_summary = project.summary |
| custom_issue_entry_url = config.custom_issue_entry_url |
| revision_url_format = ( |
| project.revision_url_format or settings.revision_url_format) |
| |
| default_query = None |
| saved_queries = None |
| |
| # Only show default query or project saved queries for project |
| # members, in case they contain sensitive information. |
| if framework_bizobj.UserIsInProject( |
| project, mc.auth.effective_ids): |
| default_query = config.member_default_query |
| |
| saved_queries = self.services.features.GetCannedQueriesByProjectID( |
| mc.cnxn, project.project_id) |
| |
| return project_objects_pb2.PresentationConfig( |
| project_thumbnail_url=project_thumbnail_url, |
| project_summary=project_summary, |
| custom_issue_entry_url=custom_issue_entry_url, |
| default_query=default_query, |
| default_col_spec=config.default_col_spec, |
| default_sort_spec=config.default_sort_spec, |
| default_x_attr=config.default_x_attr, |
| default_y_attr=config.default_y_attr, |
| saved_queries=converters.IngestSavedQueries( |
| mc.cnxn, self.services.project, saved_queries), |
| revision_url_format=revision_url_format) |
| |
| @monorail_servicer.PRPCMethod |
| def GetCustomPermissions(self, mc, request): |
| """Return the custom permissions for the given project.""" |
| project = self._GetProject(mc, request) |
| custom_permissions = permissions.GetCustomPermissions(project) |
| |
| result = projects_pb2.GetCustomPermissionsResponse( |
| permissions=custom_permissions) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def GetVisibleMembers(self, mc, request): |
| """Return the members of the project that the user can see. |
| |
| Raises: |
| PermissionException the user is not allowed to view the project members. |
| """ |
| project = self._GetProject(mc, request) |
| if not permissions.CanViewContributorList(mc, project): |
| raise permissions.PermissionException( |
| 'User is not allowed to view the project members') |
| |
| users_by_id = tracker_helpers.GetVisibleMembers(mc, project, self.services) |
| |
| sorted_user_ids = sorted( |
| users_by_id, key=lambda uid: users_by_id[uid].email) |
| user_refs = converters.ConvertUserRefs( |
| sorted_user_ids, [], users_by_id, True) |
| sorted_group_ids = sorted( |
| (uv.user_id for uv in users_by_id.values() if uv.is_group), |
| key=lambda uid: users_by_id[uid].email) |
| group_refs = converters.ConvertUserRefs( |
| sorted_group_ids, [], users_by_id, True) |
| |
| result = projects_pb2.GetVisibleMembersResponse( |
| user_refs=user_refs, group_refs=group_refs) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def GetLabelOptions(self, mc, request): |
| """Return the label options for autocomplete for the given project.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| config = we.GetProjectConfig(project.project_id) |
| |
| label_options = tracker_helpers.GetLabelOptions( |
| config, permissions.GetCustomPermissions(project)) |
| label_defs = [ |
| project_objects_pb2.LabelDef( |
| label=label['name'], |
| docstring=label['doc']) |
| for label in label_options] |
| |
| result = projects_pb2.GetLabelOptionsResponse( |
| label_options=label_defs, |
| exclusive_label_prefixes=config.exclusive_label_prefixes) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def ListStatuses(self, mc, request): |
| """Return all well-known statuses in the specified project.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| config = we.GetProjectConfig(project.project_id) |
| |
| status_defs = [ |
| converters.ConvertStatusDef(sd) |
| for sd in config.well_known_statuses] |
| statuses_offer_merge = [ |
| converters.ConvertStatusRef(sd.status, None, config) |
| for sd in config.well_known_statuses |
| if sd.status in config.statuses_offer_merge] |
| |
| result = projects_pb2.ListStatusesResponse( |
| status_defs=status_defs, |
| statuses_offer_merge=statuses_offer_merge, |
| restrict_to_known=config.restrict_to_known) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def ListComponents(self, mc, request): |
| """Return all component defs in the specified project.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| config = we.GetProjectConfig(project.project_id) |
| |
| with mc.profiler.Phase('making user views'): |
| users_by_id = {} |
| if request.include_admin_info: |
| users_involved = tracker_bizobj.UsersInvolvedInConfig(config) |
| users_by_id = framework_views.MakeAllUserViews( |
| mc.cnxn, self.services.user, users_involved) |
| framework_views.RevealAllEmailsToMembers( |
| mc.cnxn, self.services, mc.auth, users_by_id, project) |
| |
| with mc.profiler.Phase('looking up labels'): |
| labels_by_id = {} |
| if request.include_admin_info: |
| label_ids = tracker_bizobj.LabelIDsInvolvedInConfig(config) |
| labels_by_id = { |
| label_id: self.services.config.LookupLabel( |
| mc.cnxn, config.project_id, label_id) |
| for label_id in label_ids} |
| |
| component_defs = [ |
| converters.ConvertComponentDef( |
| cd, users_by_id, labels_by_id, request.include_admin_info) |
| for cd in config.component_defs] |
| |
| result = projects_pb2.ListComponentsResponse( |
| component_defs=component_defs) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def ListFields(self, mc, request): |
| """List all fields for the specified project.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| config = we.GetProjectConfig(project.project_id) |
| |
| users_by_id = {} |
| users_for_perm = {} |
| # Only look for members if user choices are requested and there are user |
| # fields that need permissions. |
| if request.include_user_choices: |
| perms_needed = { |
| fd.needs_perm |
| for fd in config.field_defs |
| if fd.needs_perm and not fd.is_deleted} |
| if perms_needed: |
| users_by_id = tracker_helpers.GetVisibleMembers( |
| mc, project, self.services) |
| effective_ids_by_user = self.services.usergroup.LookupAllMemberships( |
| mc.cnxn, users_by_id) |
| users_for_perm = project_helpers.UsersWithPermsInProject( |
| project, perms_needed, users_by_id, effective_ids_by_user) |
| |
| field_defs = [ |
| converters.ConvertFieldDef( |
| fd, users_for_perm.get(fd.needs_perm, []), users_by_id, config, |
| request.include_admin_info) |
| for fd in config.field_defs |
| if not fd.is_deleted] |
| |
| result = projects_pb2.ListFieldsResponse(field_defs=field_defs) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def GetProjectStarCount(self, mc, request): |
| """Get the star count for the specified project.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| star_count = we.GetProjectStarCount(project.project_id) |
| |
| result = projects_pb2.GetProjectStarCountResponse(star_count=star_count) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def StarProject(self, mc, request): |
| """Star the specified project.""" |
| project = self._GetProject(mc, request) |
| |
| with work_env.WorkEnv(mc, self.services) as we: |
| we.StarProject(project.project_id, request.starred) |
| star_count = we.GetProjectStarCount(project.project_id) |
| |
| result = projects_pb2.StarProjectResponse(star_count=star_count) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def CheckProjectName(self, mc, request): |
| """Check that a project name is valid and not already in use.""" |
| with work_env.WorkEnv(mc, self.services) as we: |
| error = we.CheckProjectName(request.project_name) |
| result = projects_pb2.CheckProjectNameResponse(error=error) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def CheckComponentName(self, mc, request): |
| """Check that the component name is valid and not already in use.""" |
| project = self._GetProject(mc, request) |
| with work_env.WorkEnv(mc, self.services) as we: |
| error = we.CheckComponentName( |
| project.project_id, request.parent_path, request.component_name) |
| result = projects_pb2.CheckComponentNameResponse(error=error) |
| return result |
| |
| @monorail_servicer.PRPCMethod |
| def CheckFieldName(self, mc, request): |
| """Check that a field name is valid and not already in use.""" |
| project = self._GetProject(mc, request) |
| with work_env.WorkEnv(mc, self.services) as we: |
| error = we.CheckFieldName(project.project_id, request.field_name) |
| result = projects_pb2.CheckFieldNameResponse(error=error) |
| return result |