Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/api/projects_servicer.py b/api/projects_servicer.py
new file mode 100644
index 0000000..640a433
--- /dev/null
+++ b/api/projects_servicer.py
@@ -0,0 +1,341 @@
+# 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