Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/api/v3/hotlists_servicer.py b/api/v3/hotlists_servicer.py
new file mode 100644
index 0000000..2ea2a31
--- /dev/null
+++ b/api/v3/hotlists_servicer.py
@@ -0,0 +1,266 @@
+# 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()