Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/api/v3/users_servicer.py b/api/v3/users_servicer.py
new file mode 100644
index 0000000..cbf70c5
--- /dev/null
+++ b/api/v3/users_servicer.py
@@ -0,0 +1,127 @@
+# 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.
+
+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 import api_constants
+from api.v3.api_proto import users_pb2
+from api.v3.api_proto import user_objects_pb2
+from api.v3.api_proto import users_prpc_pb2
+from businesslogic import work_env
+from framework import exceptions
+
+
+class UsersServicer(monorail_servicer.MonorailServicer):
+  """Handle API requests related to User 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.
+  """
+
+  DESCRIPTION = users_prpc_pb2.UsersServiceDescription
+
+  @monorail_servicer.PRPCMethod
+  def GetUser(self, mc, request):
+    # type: (MonorailContext, GetUserRequest) ->
+    # GetUserResponse
+    """pRPC API method that implements GetUser.
+
+      Raises:
+        InputException if a name in request.name is invalid.
+        NoSuchUserException if a User is not found.
+    """
+    user_id = rnc.IngestUserName(mc.cnxn, request.name, self.services)
+
+    with work_env.WorkEnv(mc, self.services) as we:
+      user = we.GetUser(user_id)
+
+    return self.converter.ConvertUser(user)
+
+  @monorail_servicer.PRPCMethod
+  def BatchGetUsers(self, mc, request):
+    # type: (MonorailContext, BatchGetUsersRequest) ->
+    # BatchGetUsersResponse
+    """pRPC API method that implements BatchGetUsers.
+
+      Raises:
+        InputException if a name in request.names is invalid.
+        NoSuchUserException if a User is not found.
+    """
+    if len(request.names) > api_constants.MAX_BATCH_USERS:
+      raise exceptions.InputException(
+          'Requesting %d users when the allowed maximum is %d users.' %
+          (len(request.names), api_constants.MAX_BATCH_USERS))
+    user_ids = rnc.IngestUserNames(mc.cnxn, request.names, self.services)
+
+    with work_env.WorkEnv(mc, self.services) as we:
+      users = we.BatchGetUsers(user_ids)
+
+    api_users_by_id = self.converter.ConvertUsers(
+        [user.user_id for user in users])
+    api_users = [api_users_by_id[user_id] for user_id in user_ids]
+
+    return users_pb2.BatchGetUsersResponse(users=api_users)
+
+  @monorail_servicer.PRPCMethod
+  def StarProject(self, mc, request):
+    # type: (MonorailContext, StarProjectRequest) ->
+    # ProjectStar
+    """pRPC API method that implements StarProject.
+
+      Raises:
+        InputException if the project name in request.project is invalid.
+        NoSuchProjectException if no project exists with the given name.
+    """
+    project_id = rnc.IngestProjectName(mc.cnxn, request.project, self.services)
+
+    with work_env.WorkEnv(mc, self.services) as we:
+      we.StarProject(project_id, True)
+
+    user_id = mc.auth.user_id
+    star_name = rnc.ConvertProjectStarName(
+        mc.cnxn, user_id, project_id, self.services)
+
+    return user_objects_pb2.ProjectStar(name=star_name)
+
+  @monorail_servicer.PRPCMethod
+  def UnStarProject(self, mc, request):
+    # type: (MonorailContext, UnStarProjectRequest) ->
+    # Empty
+    """pRPC API method that implements UnStarProject.
+
+      Raises:
+        InputException if the project name in request.project is invalid.
+        NoSuchProjectException if no project exists with the given name.
+    """
+    project_id = rnc.IngestProjectName(mc.cnxn, request.project, self.services)
+
+    with work_env.WorkEnv(mc, self.services) as we:
+      we.StarProject(project_id, False)
+
+    return empty_pb2.Empty()
+
+  @monorail_servicer.PRPCMethod
+  def ListProjectStars(self, mc, request):
+    # type: (MonorailContext, ListProjectStarsRequest) ->
+    #   ListProjectStarsResponse
+    """pRPC API method that implements ListProjectStars.
+
+      Raises:
+        InputException: if the `page_token` or `parent` is invalid.
+        NoSuchUserException: if the User is not found.
+    """
+    user_id = rnc.IngestUserName(mc.cnxn, request.parent, self.services)
+
+    with work_env.WorkEnv(mc, self.services) as we:
+      projects = we.ListStarredProjects(user_id)
+
+    # TODO(crbug.com/monorail/7175): Add pagination logic.
+    return users_pb2.ListProjectStarsResponse(
+        project_stars=self.converter.ConvertProjectStars(user_id, projects))