Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/api/v3/projects_servicer.py b/api/v3/projects_servicer.py
new file mode 100644
index 0000000..17d6f93
--- /dev/null
+++ b/api/v3/projects_servicer.py
@@ -0,0 +1,149 @@
+# 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 api_constants
+from api.v3 import monorail_servicer
+from api.v3 import paginator
+from api.v3.api_proto import projects_pb2
+from api.v3.api_proto import projects_prpc_pb2
+from businesslogic import work_env
+
+
+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. Each method does any request-specific validation, uses work_env
+ to safely operate on business objects, and returns a response proto.
+ """
+
+ DESCRIPTION = projects_prpc_pb2.ProjectsServiceDescription
+
+ @monorail_servicer.PRPCMethod
+ def ListIssueTemplates(self, mc, request):
+ # type: (MonorailContext, ListIssueTemplatesRequest) ->
+ # ListIssueTemplatesResponse
+ """pRPC API method that implements ListIssueTemplates.
+
+ Raises:
+ InputException if the request.parent is invalid.
+ NoSuchProjectException if no project exists with the given name.
+ """
+ project_id = rnc.IngestProjectName(mc.cnxn, request.parent, self.services)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ # TODO(crbug/monorail/7614): Eliminate the need to do this lookup.
+ project = we.GetProject(project_id)
+ mc.LookupLoggedInUserPerms(project)
+ templates = we.ListProjectTemplates(project_id)
+
+ return projects_pb2.ListIssueTemplatesResponse(
+ templates=self.converter.ConvertIssueTemplates(project_id, templates))
+
+ @monorail_servicer.PRPCMethod
+ def ListComponentDefs(self, mc, request):
+ # type: (MonorailContext, ListComponentDefsRequest) ->
+ # ListComponentDefsResponse
+ """pRPC API method that implements ListComponentDefs.
+
+ Raises:
+ InputException if the request.parent is invalid.
+ NoSuchProjectException if the parent project is not found.
+ """
+ project_id = rnc.IngestProjectName(mc.cnxn, request.parent, self.services)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ # TODO(crbug/monorail/7614): Eliminate the need to do this lookup.
+ project = we.GetProject(project_id)
+ mc.LookupLoggedInUserPerms(project)
+
+ page_size = paginator.CoercePageSize(
+ request.page_size, api_constants.MAX_COMPONENTS_PER_PAGE)
+ pager = paginator.Paginator(
+ parent=request.parent, page_size=page_size)
+ list_result = we.ListComponentDefs(
+ project_id, page_size, pager.GetStart(request.page_token))
+
+ api_component_defs = self.converter.ConvertComponentDefs(
+ list_result.items, project_id)
+
+ return projects_pb2.ListComponentDefsResponse(
+ component_defs=api_component_defs,
+ next_page_token=pager.GenerateNextPageToken(list_result.next_start))
+
+ @monorail_servicer.PRPCMethod
+ def CreateComponentDef(self, mc, request):
+ # type: (MonorailContext, CreateComponentDefRequest) ->
+ # ComponentDef
+ """pRPC API method that implements CreateComponentDef.
+
+ Raises:
+ InputException if the request is invalid.
+ NoSuchUserException if any given component admins or ccs do not exist.
+ NoSuchProjectException if the parent project does not exist.
+ PermissionException if the requester is not allowed to create
+ this component.
+ """
+ project_id = rnc.IngestProjectName(mc.cnxn, request.parent, self.services)
+ admin_ids = rnc.IngestUserNames(
+ mc.cnxn, request.component_def.admins, self.services)
+ cc_ids = rnc.IngestUserNames(
+ mc.cnxn, request.component_def.ccs, self.services)
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ component_def = we.CreateComponentDef(
+ project_id, request.component_def.value,
+ request.component_def.docstring, admin_ids, cc_ids,
+ request.component_def.labels)
+
+ return self.converter.ConvertComponentDef(component_def)
+
+ @monorail_servicer.PRPCMethod
+ def DeleteComponentDef(self, mc, request):
+ # type: (MonorailContext, DeleteComponentDefRequest) -> Empty
+ """pRPC API method that implements DeleteComponentDef.
+
+ Raises:
+ InputException if the request in invalid.
+ NoSuchComponentException if the component does not exist.
+ PermissionException if the requester is not allowed to delete
+ this component.
+ NoSuchProjectException if the parent project does not exist.
+ """
+ project_id, component_id = rnc.IngestComponentDefNames(
+ mc.cnxn, [request.name], self.services)[0]
+
+ with work_env.WorkEnv(mc, self.services) as we:
+ we.DeleteComponentDef(project_id, component_id)
+
+ return empty_pb2.Empty()
+
+ @monorail_servicer.PRPCMethod
+ def ListProjects(self, mc, _):
+ # type: (MonorailContext, ListProjectsRequest) -> ListProjectsResponse
+ """pRPC API method that implements ListProjects.
+
+ Raises:
+ InputException if the request.page_token is invalid or the request does
+ not match the previous request that provided the given page_token.
+ """
+ with work_env.WorkEnv(mc, self.services) as we:
+ # 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.
+ # This method does not because it may be scoped to multiple projects.
+ allowed_project_ids = we.ListProjects()
+ projects_dict = we.GetProjects(allowed_project_ids)
+ projects = [projects_dict[proj_id] for proj_id in allowed_project_ids]
+
+ # TODO(crbug.com/monorail/7505): Add pagination logic.
+ return projects_pb2.ListProjectsResponse(
+ projects=self.converter.ConvertProjects(projects))