Merge branch 'main' into avm99963-monorail
Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266
GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/services/api_svc_v1.py b/services/api_svc_v1.py
index 8d8f238..883d69f 100644
--- a/services/api_svc_v1.py
+++ b/services/api_svc_v1.py
@@ -1,7 +1,6 @@
-# Copyright 2016 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
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
"""API service.
@@ -45,9 +44,9 @@
from framework import ratelimiter
from framework import sql
from project import project_helpers
-from proto import api_pb2_v1
-from proto import project_pb2
-from proto import tracker_pb2
+from mrproto import api_pb2_v1
+from mrproto import project_pb2
+from mrproto import tracker_pb2
from search import frontendsearchpipeline
from services import api_pb2_v1_helpers
from services import client_config_svc
@@ -59,6 +58,7 @@
from tracker import tracker_bizobj
from tracker import tracker_constants
from tracker import tracker_helpers
+from redirect import redirect_utils
from infra_libs import ts_mon
@@ -284,7 +284,9 @@
if not project:
raise exceptions.NoSuchProjectException(
'Project %s does not exist' % project_name)
- if project.state != project_pb2.ProjectState.LIVE:
+ # Allow to view non-live projects that were migrated.
+ if (project.state != project_pb2.ProjectState.LIVE and
+ project_name not in redirect_utils.PROJECT_REDIRECT_MAP):
raise permissions.PermissionException(
'API may not access project %s because it is not live'
% project_name)
@@ -314,7 +316,6 @@
@endpoints.api(name=ENDPOINTS_API_NAME, version='v1',
description='Monorail API to manage issues.',
- auth_level=endpoints.AUTH_LEVEL.NONE,
allowed_client_ids=endpoints.SKIP_CLIENT_ID_CHECK,
documentation=DOC_URL)
class MonorailApi(remote.Service):
@@ -414,7 +415,7 @@
http_method='POST',
name='issues.comments.insert')
def issues_comments_insert(self, mar, request):
- # type (...) -> proto.api_pb2_v1.IssuesCommentsInsertResponse
+ # type (...) -> mrproto.api_pb2_v1.IssuesCommentsInsertResponse
"""Add a comment."""
# Because we will modify issues, load from DB rather than cache.
issue = self._services.issue.GetIssueByLocalID(
@@ -429,7 +430,7 @@
# Temporary block on updating approval subfields.
if request.updates and request.updates.fieldValues:
- fds_by_name = {fd.field_name.lower():fd for fd in mar.config.field_defs}
+ fds_by_name = {fd.field_name.lower(): fd for fd in mar.config.field_defs}
for fv in request.updates.fieldValues:
# Checking for fv.approvalName is unreliable since it can be removed.
fd = fds_by_name.get(fv.fieldName.lower())
@@ -485,22 +486,46 @@
mar.cnxn, updates_dict['cc_remove']).values())
updates_dict['labels_add'], updates_dict['labels_remove'] = (
api_pb2_v1_helpers.split_remove_add(request.updates.labels))
+
+ field_helpers.ValidateLabels(
+ mar.cnxn,
+ self._services,
+ mar.project_id,
+ updates_dict.get('labels_add', []),
+ ezt_errors=mar.errors)
+ if mar.errors.AnyErrors():
+ raise endpoints.BadRequestException(
+ 'Invalid field values: %s' % mar.errors.labels)
+
blocked_on_add_strs, blocked_on_remove_strs = (
api_pb2_v1_helpers.split_remove_add(request.updates.blockedOn))
- updates_dict['blocked_on_add'] = api_pb2_v1_helpers.issue_global_ids(
- blocked_on_add_strs, issue.project_id, mar,
- self._services)
- updates_dict['blocked_on_remove'] = api_pb2_v1_helpers.issue_global_ids(
- blocked_on_remove_strs, issue.project_id, mar,
- self._services)
blocking_add_strs, blocking_remove_strs = (
api_pb2_v1_helpers.split_remove_add(request.updates.blocking))
- updates_dict['blocking_add'] = api_pb2_v1_helpers.issue_global_ids(
- blocking_add_strs, issue.project_id, mar,
- self._services)
- updates_dict['blocking_remove'] = api_pb2_v1_helpers.issue_global_ids(
- blocking_remove_strs, issue.project_id, mar,
- self._services)
+ blocked_on_add_iids = api_pb2_v1_helpers.issue_global_ids(
+ blocked_on_add_strs, issue.project_id, mar, self._services)
+ blocked_on_remove_iids = api_pb2_v1_helpers.issue_global_ids(
+ blocked_on_remove_strs, issue.project_id, mar, self._services)
+ blocking_add_iids = api_pb2_v1_helpers.issue_global_ids(
+ blocking_add_strs, issue.project_id, mar, self._services)
+ blocking_remove_iids = api_pb2_v1_helpers.issue_global_ids(
+ blocking_remove_strs, issue.project_id, mar, self._services)
+ all_block = (
+ blocked_on_add_iids + blocked_on_remove_iids + blocking_add_iids +
+ blocking_remove_iids)
+ for iid in all_block:
+ # Because we will modify issues, load from DB rather than cache.
+ issue = self._services.issue.GetIssue(mar.cnxn, iid, use_cache=False)
+ project = self._services.project.GetProjectByName(
+ mar.cnxn, issue.project_name)
+ if not tracker_helpers.CanEditProjectIssue(mar, project, issue,
+ mar.granted_perms):
+ raise permissions.PermissionException(
+ 'User is not allowed to block with issue (%s, %d)' %
+ (issue.project_name, issue.local_id))
+ updates_dict['blocked_on_add'] = blocked_on_add_iids
+ updates_dict['blocked_on_remove'] = blocked_on_remove_iids
+ updates_dict['blocking_add'] = blocking_add_iids
+ updates_dict['blocking_remove'] = blocking_remove_iids
components_add_strs, components_remove_strs = (
api_pb2_v1_helpers.split_remove_add(request.updates.components))
updates_dict['components_add'] = (
@@ -518,12 +543,11 @@
merge_into_issue = self._services.issue.GetIssueByLocalID(
mar.cnxn, merge_into_project.project_id, merge_local_id,
use_cache=False)
- merge_allowed = tracker_helpers.IsMergeAllowed(
- merge_into_issue, mar, self._services)
- if not merge_allowed:
+ if not tracker_helpers.CanEditProjectIssue(
+ mar, merge_into_project, merge_into_issue, mar.granted_perms):
raise permissions.PermissionException(
- 'User is not allowed to merge into issue %s:%s' %
- (merge_into_issue.project_name, merge_into_issue.local_id))
+ 'User is not allowed to merge into issue %s:%s' %
+ (merge_into_issue.project_name, merge_into_issue.local_id))
updates_dict['merged_into'] = merge_into_issue.issue_id
(updates_dict['field_vals_add'], updates_dict['field_vals_remove'],
updates_dict['fields_clear'], updates_dict['fields_labels_add'],
@@ -730,7 +754,7 @@
http_method='POST',
name='approvals.comments.insert')
def approvals_comments_insert(self, mar, request):
- # type (...) -> proto.api_pb2_v1.ApprovalsCommentsInsertResponse
+ # type (...) -> mrproto.api_pb2_v1.ApprovalsCommentsInsertResponse
"""Add an approval comment."""
approval_fd = tracker_bizobj.FindFieldDef(
request.approvalName, mar.config)
@@ -769,8 +793,10 @@
if request.approvalUpdates.fieldValues:
# Block updating field values that don't belong to the approval.
approvals_fds_by_name = {
- fd.field_name.lower():fd for fd in mar.config.field_defs
- if fd.approval_id == approval_fd.field_id}
+ fd.field_name.lower(): fd
+ for fd in mar.config.field_defs
+ if fd.approval_id == approval_fd.field_id
+ }
for fv in request.approvalUpdates.fieldValues:
if approvals_fds_by_name.get(fv.fieldName.lower()) is None:
raise endpoints.BadRequestException(
@@ -804,7 +830,6 @@
raise permissions.PermissionException(
'User is not allowed to make this status change')
updates_dict['status'] = status
- logging.info(time.time)
approval_delta = tracker_bizobj.MakeApprovalDelta(
updates_dict.get('status'), mar.auth.user_id,
updates_dict.get('approver_ids_add', []),
@@ -903,8 +928,13 @@
issue = self._services.issue.GetIssueByLocalID(
mar.cnxn, mar.project_id, request.issueId)
+ with work_env.WorkEnv(mar, self._services) as we:
+ migrated_id = we.GetIssueMigratedID(
+ request.projectId, request.issueId, issue.labels)
+
return api_pb2_v1_helpers.convert_issue(
- api_pb2_v1.IssuesGetInsertResponse, issue, mar, self._services)
+ api_pb2_v1.IssuesGetInsertResponse, issue, mar, self._services,
+ migrated_id)
@monorail_api_method(
api_pb2_v1.ISSUES_INSERT_REQUEST_RESOURCE_CONTAINER,
@@ -941,6 +971,17 @@
fields_add, _, _, fields_labels, _ = (
api_pb2_v1_helpers.convert_field_values(
request.fieldValues, mar, self._services))
+
+ field_helpers.ValidateLabels(
+ mar.cnxn,
+ self._services,
+ mar.project_id,
+ fields_labels,
+ ezt_errors=mar.errors)
+ if mar.errors.AnyErrors():
+ raise endpoints.BadRequestException(
+ 'Invalid field values: %s' % mar.errors.labels)
+
field_helpers.ValidateCustomFields(
mar.cnxn, self._services, fields_add, mar.config, mar.project,
ezt_errors=mar.errors)
@@ -1190,8 +1231,7 @@
name='components.create')
def components_create(self, mar, request):
"""Create a component."""
- if not mar.perms.CanUsePerm(
- permissions.EDIT_PROJECT, mar.auth.effective_ids, mar.project, []):
+ if not permissions.CanEditProjectConfig(mar, self._services):
raise permissions.PermissionException(
'User is not allowed to create components for this project')
@@ -1207,8 +1247,8 @@
if not parent_def:
raise exceptions.NoSuchComponentException(
'Parent component %s does not exist.' % parent_path)
- if not permissions.CanEditComponentDef(
- mar.auth.effective_ids, mar.perms, mar.project, parent_def, config):
+ if not permissions.CanEditComponentDef(mar, self._services, parent_def,
+ config):
raise permissions.PermissionException(
'User is not allowed to add a subcomponent to component %s' %
parent_path)
@@ -1266,8 +1306,8 @@
mar.auth.effective_ids, mar.perms, mar.project, component_def):
raise permissions.PermissionException(
'User is not allowed to view this component %s' % component_path)
- if not permissions.CanEditComponentDef(
- mar.auth.effective_ids, mar.perms, mar.project, component_def, config):
+ if not permissions.CanEditComponentDef(mar, self._services, component_def,
+ config):
raise permissions.PermissionException(
'User is not allowed to delete this component %s' % component_path)
@@ -1302,8 +1342,8 @@
mar.auth.effective_ids, mar.perms, mar.project, component_def):
raise permissions.PermissionException(
'User is not allowed to view this component %s' % component_path)
- if not permissions.CanEditComponentDef(
- mar.auth.effective_ids, mar.perms, mar.project, component_def, config):
+ if not permissions.CanEditComponentDef(mar, self._services, component_def,
+ config):
raise permissions.PermissionException(
'User is not allowed to edit this component %s' % component_path)