Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/api/features_servicer.py b/api/features_servicer.py
new file mode 100644
index 0000000..2ae49c5
--- /dev/null
+++ b/api/features_servicer.py
@@ -0,0 +1,323 @@
+# 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
+
+from api import monorail_servicer
+from api import converters
+from api.api_proto import common_pb2
+from api.api_proto import features_pb2
+from api.api_proto import features_prpc_pb2
+from businesslogic import work_env
+from features import component_helpers
+from features import features_bizobj
+from framework import exceptions
+from framework import framework_bizobj
+from framework import framework_views
+from framework import paginate
+from framework import permissions
+from services import features_svc
+from tracker import tracker_bizobj
+
+
+class FeaturesServicer(monorail_servicer.MonorailServicer):
+ """Handle API requests related to Features 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 = features_prpc_pb2.FeaturesServiceDescription
+
+ @monorail_servicer.PRPCMethod
+ def ListHotlistsByUser(self, mc, request):
+ """Return the hotlists for the given user."""
+ user_id = converters.IngestUserRef(
+ mc.cnxn, request.user, self.services.user)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ hotlists = we.ListHotlistsByUser(user_id)
+
+ with mc.profiler.Phase('making user views'):
+ users_involved = features_bizobj.UsersInvolvedInHotlists(hotlists)
+ 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)
+
+ converted_hotlists = [
+ converters.ConvertHotlist(hotlist, users_by_id)
+ for hotlist in hotlists]
+
+ result = features_pb2.ListHotlistsByUserResponse(
+ hotlists=converted_hotlists)
+
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def ListHotlistsByIssue(self, mc, request):
+ # type: (MonorailConnection, proto.features.ListHotlistsByIssueRequest) ->
+ # proto.features.ListHotlistsByIssueResponse
+ """Return the hotlists the given issue is part of."""
+ issue_id = converters.IngestIssueRefs(
+ mc.cnxn, [request.issue], self.services)[0]
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ project = we.GetProjectByName(request.issue.project_name)
+ mc.LookupLoggedInUserPerms(project)
+ hotlists = we.ListHotlistsByIssue(issue_id)
+
+ # Reduce spam by only showing hotlists that belong to a project member.
+ project_hotlists = [
+ hotlist for hotlist in hotlists if framework_bizobj.UserIsInProject(
+ project, set(hotlist.owner_ids + hotlist.editor_ids)) or
+ features_bizobj.UserIsInHotlist(hotlist, mc.auth.effective_ids)
+ ]
+ if project_hotlists and len(hotlists) / len(project_hotlists) > 10:
+ logging.warning(
+ 'Unusual hotlist activity in %s:%s' %
+ (request.issue.project_name, issue_id))
+
+ with mc.profiler.Phase('making user views'):
+ users_involved = features_bizobj.UsersInvolvedInHotlists(project_hotlists)
+ 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)
+
+ converted_hotlists = [
+ converters.ConvertHotlist(hotlist, users_by_id)
+ for hotlist in project_hotlists
+ ]
+
+ result = features_pb2.ListHotlistsByIssueResponse(
+ hotlists=converted_hotlists)
+
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def ListRecentlyVisitedHotlists(self, mc, _request):
+ """Return the recently visited hotlists for the logged in user."""
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ hotlists = we.ListRecentlyVisitedHotlists()
+
+ with mc.profiler.Phase('making user views'):
+ users_involved = features_bizobj.UsersInvolvedInHotlists(hotlists)
+ 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)
+
+ converted_hotlists = [
+ converters.ConvertHotlist(hotlist, users_by_id)
+ for hotlist in hotlists]
+
+ result = features_pb2.ListRecentlyVisitedHotlistsResponse(
+ hotlists=converted_hotlists)
+
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def ListStarredHotlists(self, mc, _request):
+ """Return the starred hotlists for the logged in user."""
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ hotlists = we.ListStarredHotlists()
+
+ with mc.profiler.Phase('maknig user views'):
+ users_involved = features_bizobj.UsersInvolvedInHotlists(hotlists)
+ 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)
+
+ converted_hotlists = [
+ converters.ConvertHotlist(hotlist, users_by_id)
+ for hotlist in hotlists]
+
+ result = features_pb2.ListStarredHotlistsResponse(
+ hotlists=converted_hotlists)
+
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def GetHotlistStarCount(self, mc, request):
+ """Get the star count for the specified hotlist."""
+ hotlist_id = converters.IngestHotlistRef(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_ref)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ star_count = we.GetHotlistStarCount(hotlist_id)
+
+ result = features_pb2.GetHotlistStarCountResponse(star_count=star_count)
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def StarHotlist(self, mc, request):
+ """Star the specified hotlist."""
+ hotlist_id = converters.IngestHotlistRef(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_ref)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ we.StarHotlist(hotlist_id, request.starred)
+ star_count = we.GetHotlistStarCount(hotlist_id)
+
+ result = features_pb2.StarHotlistResponse(star_count=star_count)
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def GetHotlist(self, mc, request):
+ """Get the Hotlist metadata for the specified hotlist."""
+ hotlist_id = converters.IngestHotlistRef(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_ref)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ hotlist = we.GetHotlist(hotlist_id)
+
+ with mc.profiler.Phase('making user views'):
+ users_involved = features_bizobj.UsersInvolvedInHotlists([hotlist])
+ 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)
+
+ converted_hotlist = converters.ConvertHotlist(hotlist, users_by_id)
+ return features_pb2.GetHotlistResponse(hotlist=converted_hotlist)
+
+ @monorail_servicer.PRPCMethod
+ def CreateHotlist(self, mc, request):
+ """Create a new hotlist."""
+ editor_ids = converters.IngestUserRefs(
+ mc.cnxn, request.editor_refs, self.services.user)
+ issue_ids = converters.IngestIssueRefs(
+ mc.cnxn, request.issue_refs, self.services)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ we.CreateHotlist(
+ request.name, request.summary, request.description, editor_ids,
+ issue_ids, request.is_private, '')
+
+ result = features_pb2.CreateHotlistResponse()
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def CheckHotlistName(self, mc, request):
+ """Check that a hotlist name is valid and not already in use."""
+ with work_env.WorkEnv(mc, self.services) as we:
+ error = we.CheckHotlistName(request.name)
+ result = features_pb2.CheckHotlistNameResponse(error=error)
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def RemoveIssuesFromHotlists(self, mc, request):
+ """Remove the given issues from the given hotlists."""
+ hotlist_ids = converters.IngestHotlistRefs(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_refs)
+ issue_ids = converters.IngestIssueRefs(
+ mc.cnxn, request.issue_refs, self.services)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ we.RemoveIssuesFromHotlists(hotlist_ids, issue_ids)
+
+ result = features_pb2.RemoveIssuesFromHotlistsResponse()
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def AddIssuesToHotlists(self, mc, request):
+ """Add the given issues to the given hotlists."""
+ hotlist_ids = converters.IngestHotlistRefs(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_refs)
+ issue_ids = converters.IngestIssueRefs(
+ mc.cnxn, request.issue_refs, self.services)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ mc.LookupLoggedInUserPerms(None)
+ we.AddIssuesToHotlists(hotlist_ids, issue_ids, request.note)
+
+ result = features_pb2.AddIssuesToHotlistsResponse()
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def RerankHotlistIssues(self, mc, request):
+ """Rerank issues in the given hotlist."""
+ hotlist_id = converters.IngestHotlistRef(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_ref)
+ moved_issue_ids = converters.IngestIssueRefs(
+ mc.cnxn, request.moved_refs, self.services)
+ [target_issue_id] = converters.IngestIssueRefs(
+ mc.cnxn, [request.target_ref], self.services)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ we.RerankHotlistIssues(
+ hotlist_id, moved_issue_ids, target_issue_id, request.split_above)
+
+ # TODO(jojwang): return updated hotlist items.
+ with mc.profiler.Phase('converting to response objects'):
+ result = features_pb2.RerankHotlistIssuesResponse()
+
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def UpdateHotlistIssueNote(self, mc, request):
+ """Update the note for the given issue in the given hotlist."""
+ hotlist_id = converters.IngestHotlistRef(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_ref)
+ issue_id = converters.IngestIssueRefs(
+ mc.cnxn, [request.issue_ref], self.services)[0]
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ project = we.GetProjectByName(request.issue_ref.project_name)
+ mc.LookupLoggedInUserPerms(project)
+ we.UpdateHotlistIssueNote(hotlist_id, issue_id, request.note)
+
+ result = features_pb2.UpdateHotlistIssueNoteResponse()
+ return result
+
+ @monorail_servicer.PRPCMethod
+ def DeleteHotlist(self, mc, request):
+ """Delete the given hotlist"""
+ hotlist_id = converters.IngestHotlistRef(
+ mc.cnxn, self.services.user, self.services.features,
+ request.hotlist_ref)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ we.DeleteHotlist(hotlist_id)
+
+ return features_pb2.DeleteHotlistResponse()
+
+ # TODO(https://crbug.com/monorail/7515): Replace or delete PredictComponent.
+ @monorail_servicer.PRPCMethod
+ def PredictComponent(self, mc, request):
+ """Predict the component of an issue based on the given text."""
+ with work_env.WorkEnv(mc, self.services) as we:
+ project = we.GetProjectByName(request.project_name)
+ config = we.GetProjectConfig(project.project_id)
+
+ component_ref = None
+ component_id = component_helpers.PredictComponent(request.text, config)
+
+ if component_id:
+ component_ref = converters.ConvertComponentRef(component_id, config)
+
+ result = features_pb2.PredictComponentResponse(component_ref=component_ref)
+ return result