blob: 640a43356b3ab10ebc58cbb988abb3ff603836d1 [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
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