text: # Copyright 2020 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
  
  from google.protobuf import empty_pb2
  
  from api import resource_name_converters as rnc
  from api.v3 import monorail_servicer
  from api.v3.api_proto import feature_objects_pb2
  from api.v3.api_proto import hotlists_pb2
  from api.v3.api_proto import hotlists_prpc_pb2
  from businesslogic import work_env
  from framework import exceptions
  from features import features_constants
  from tracker import tracker_constants
  
  
  class HotlistsServicer(monorail_servicer.MonorailServicer):
    """Handle API requests related to Hotlist objects.
    Each API request is implemented with a method as defined in the
    .proto file. Each method does any request-specific validation, uses work_env
    to safely operate on business objects, and returns a response proto.
    """
    # NOTE(crbug/monorail/7614): Until the referenced cleanup is complete,
    # all servicer methods that are scoped to a single Project need to call
    # mc.LookupLoggedInUserPerms.
    # Methods in this file do not because hotlists can span projects.
  
    DESCRIPTION = hotlists_prpc_pb2.HotlistsServiceDescription
  
    @monorail_servicer.PRPCMethod
    def ListHotlistItems(self, mc, request):
      # type: (MonorailContext, ListHotlistItemsRequest) ->
      #     ListHotlistItemsResponse
      """pRPC API method that implements ListHotlistItems.
  
        Raises:
          NoSuchHotlistException if the hotlist is not found.
          PermissionException if the user is not allowed to view the hotlist.
          InputException if the request.page_token is invalid, the request does
            not match the previous request that provided the given page_token, or
            the page_size is a negative value.
      """
      hotlist_id = rnc.IngestHotlistName(request.parent)
      if request.page_size < 0:
        raise exceptions.InputException('`page_size` cannot be negative.')
      page_size = request.page_size
      if (not request.page_size or
          request.page_size > features_constants.DEFAULT_RESULTS_PER_PAGE):
        page_size = features_constants.DEFAULT_RESULTS_PER_PAGE
  
      # TODO(crbug/monorail/7104): take start from request.page_token
      start = 0
      sort_spec = request.order_by.replace(',', ' ')
  
      with work_env.WorkEnv(mc, self.services) as we:
        list_result = we.ListHotlistItems(
            hotlist_id, page_size, start,
            tracker_constants.ALL_ISSUES_CAN, sort_spec, '')
  
      # TODO(crbug/monorail/7104): plug in next_page_token when it's been
      # implemented.
      next_page_token = ''
      return hotlists_pb2.ListHotlistItemsResponse(
          items=self.converter.ConvertHotlistItems(hotlist_id, list_result.items),
          next_page_token=next_page_token)
  
  
    @monorail_servicer.PRPCMethod
    def RerankHotlistItems(self, mc, request):
      # type: (MonorailContext, RerankHotlistItemsRequest) -> Empty
      """pRPC API method that implements RerankHotlistItems.
  
      Raises:
        NoSuchHotlistException if the hotlist is not found.
        PermissionException if the user is not allowed to rerank the hotlist.
        InputException if request.target_position is invalid or
          request.hotlist_items is empty or contains invalid items.
        NoSuchIssueException if hotlist item does not exist.
      """
  
      hotlist_id = rnc.IngestHotlistName(request.name)
      moved_issue_ids = rnc.IngestHotlistItemNames(
          mc.cnxn, request.hotlist_items, self.services)
  
      with work_env.WorkEnv(mc, self.services) as we:
        we.RerankHotlistItems(
            hotlist_id, moved_issue_ids, request.target_position)
  
      return empty_pb2.Empty()
  
  
    @monorail_servicer.PRPCMethod
    def RemoveHotlistItems(self, mc, request):
      # type: (MonorailContext, RemoveHotlistItemsRequest) -> Empty
      """pPRC API method that implements RemoveHotlistItems.
  
      Raises:
        NoSuchHotlistException if the hotlist is not found.
        PermissionException if the user is not allowed to edit the hotlist.
        InputException if the items to be removed are not found in the hotlist.
      """
  
      hotlist_id = rnc.IngestHotlistName(request.parent)
      remove_issue_ids = rnc.IngestIssueNames(
          mc.cnxn, request.issues, self.services)
  
      with work_env.WorkEnv(mc, self.services) as we:
        we.RemoveHotlistItems(hotlist_id, remove_issue_ids)
  
      return empty_pb2.Empty()
  
  
    @monorail_servicer.PRPCMethod
    def AddHotlistItems(self, mc, request):
      # type: (MonorailContext, AddHotlistItemsRequest) -> Empty
      """pRPC API method that implements AddHotlistItems.
  
      Raises:
        NoSuchHotlistException if the hotlist is not found.
        PermissionException if the user is not allowed to edit the hotlist.
        InputException if the request.target_position is invalid or the given
          list of issues to add is empty or invalid.
      """
      hotlist_id = rnc.IngestHotlistName(request.parent)
      new_issue_ids = rnc.IngestIssueNames(mc.cnxn, request.issues, self.services)
  
      with work_env.WorkEnv(mc, self.services) as we:
        we.AddHotlistItems(hotlist_id, new_issue_ids, request.target_position)
  
      return empty_pb2.Empty()
  
  
    @monorail_servicer.PRPCMethod
    def RemoveHotlistEditors(self, mc, request):
      # type: (MonorailContext, RemoveHotlistEditorsRequest) -> Empty
      """pPRC API method that implements RemoveHotlistEditors.
  
      Raises:
        NoSuchHotlistException if the hotlist is not found.
        PermissionException if the user is not allowed to edit the hotlist.
        InputException if the editors to be removed are not found in the hotlist.
      """
  
      hotlist_id = rnc.IngestHotlistName(request.name)
      remove_user_ids = rnc.IngestUserNames(
          mc.cnxn, request.editors, self.services)
  
      with work_env.WorkEnv(mc, self.services) as we:
        we.RemoveHotlistEditors(hotlist_id, remove_user_ids)
  
      return empty_pb2.Empty()
  
  
    @monorail_servicer.PRPCMethod
    def GetHotlist(self, mc, request):
      # type: (MonorailContext, GetHotlistRequest) -> Hotlist
      """pRPC API method that implements GetHotlist.
  
      Raises:
        InputException if the given name does not have a valid format.
        NoSuchHotlistException if the hotlist is not found.
        PermissionException if the user is not allowed to view the hotlist.
      """
  
      hotlist_id = rnc.IngestHotlistName(request.name)
  
      with work_env.WorkEnv(mc, self.services) as we:
        hotlist = we.GetHotlist(hotlist_id)
  
      return self.converter.ConvertHotlist(hotlist)
  
    @monorail_servicer.PRPCMethod
    def GatherHotlistsForUser(self, mc, request):
      # type: (MonorailContext, GatherHotlistsForUserRequest)
      #   -> GatherHotlistsForUserResponse
      """pRPC API method that implements GatherHotlistsForUser.
  
      Raises:
        NoSuchUserException if the user is not found.
        InputException if some request parameters are invalid.
      """
  
      user_id = rnc.IngestUserName(mc.cnxn, request.user, self.services)
  
      with work_env.WorkEnv(mc, self.services) as we:
        hotlists = we.ListHotlistsByUser(user_id)
  
      return hotlists_pb2.GatherHotlistsForUserResponse(
          hotlists=self.converter.ConvertHotlists(hotlists))
  
    @monorail_servicer.PRPCMethod
    def UpdateHotlist(self, mc, request):
      # type: (MonorailContext, UpdateHotlistRequest) -> UpdateHotlistResponse
      """pRPC API method that implements UpdateHotlist.
  
      Raises:
        NoSuchHotlistException if the hotlist is not found.
        PermissionException if the user is not allowed to make this update.
        InputException if some request parameters are required and missing or
          invalid.
      """
      if not request.update_mask:
        raise exceptions.InputException('No paths given in `update_mask`.')
      if not request.hotlist:
        raise exceptions.InputException('No `hotlist` param given.')
  
      if not request.update_mask.IsValidForDescriptor(
          feature_objects_pb2.Hotlist.DESCRIPTOR):
        raise exceptions.InputException('Invalid `update_mask` for `hotlist`')
  
      hotlist_id = rnc.IngestHotlistName(request.hotlist.name)
  
      update_args = {}
      hotlist = request.hotlist
      for path in request.update_mask.paths:
        if path == 'display_name':
          update_args['hotlist_name'] = hotlist.display_name
        elif path == 'owner':
          owner_id = rnc.IngestUserName(mc.cnxn, hotlist.owner, self.services)
          update_args['owner_id'] = owner_id
        elif path == 'editors':
          add_editor_ids = rnc.IngestUserNames(
              mc.cnxn, hotlist.editors, self.services)
          update_args['add_editor_ids'] = add_editor_ids
        elif path == 'summary':
          update_args['summary'] = hotlist.summary
        elif path == 'description':
          update_args['description'] = hotlist.description
        elif path == 'hotlist_privacy':
          update_args['is_private'] = (
              hotlist.hotlist_privacy == feature_objects_pb2.Hotlist
              .HotlistPrivacy.Value('PRIVATE'))
        elif path == 'default_columns':
          update_args[
              'default_col_spec'] = self.converter.IngestIssuesListColumns(
                  hotlist.default_columns)
  
      with work_env.WorkEnv(mc, self.services) as we:
        we.UpdateHotlist(hotlist_id, **update_args)
        hotlist = we.GetHotlist(hotlist_id, use_cache=False)
  
      return self.converter.ConvertHotlist(hotlist)
  
    @monorail_servicer.PRPCMethod
    def DeleteHotlist(self, mc, request):
      # type: (MonorailContext, GetHotlistRequest) -> Empty
      """pRPC API method that implements DeleteHotlist.
  
      Raises:
        InputException if the given name does not have a valid format.
        NoSuchHotlistException if the hotlist is not found.
        PermissionException if the user is not allowed to delete the hotlist.
      """
  
      hotlist_id = rnc.IngestHotlistName(request.name)
  
      with work_env.WorkEnv(mc, self.services) as we:
        we.DeleteHotlist(hotlist_id)
  
      return empty_pb2.Empty()
  