def _GetProjectIssueIDsAndConfig(self, mc, issue_refs, use_cache=True): """Get info from a single project for repeated issue_refs requests.""" project_names = set() local_ids = [] for issue_ref in issue_refs: if not issue_ref.local_id: raise exceptions.InputException('Param `local_id` required.') local_ids.append(issue_ref.local_id) if issue_ref.project_name: project_names.add(issue_ref.project_name) if not project_names: raise exceptions.InputException('Param `project_name` required.') if len(project_names) != 1: raise exceptions.InputException( 'This method does not support cross-project issue_refs.') project_name = project_names.pop() with work_env.WorkEnv(mc, self.services, phase='getting P, I ids, C') as we: project = we.GetProjectByName(project_name, use_cache=use_cache) mc.LookupLoggedInUserPerms(project) config = we.GetProjectConfig(project.project_id, use_cache=use_cache) project_local_id_pairs = [(project.project_id, local_id) for local_id in local_ids] issue_ids, _misses = self.services.issue.LookupIssueIDs( mc.cnxn, project_local_id_pairs) return project, issue_ids, config
def __init__(self, main_clause, insert_args=None, update_args=None, duplicate_update_cols=None, or_where_conds=False): self.main_clause = main_clause # E.g., SELECT or DELETE self.or_where_conds = or_where_conds self.insert_args = insert_args or [] # For INSERT statements for row_value in self.insert_args: if not all(_IsValidDBValue(val) for val in row_value): raise exceptions.InputException('Invalid DB value %r' % (row_value, )) self.update_args = update_args or [] # For UPDATEs for val in self.update_args: if not _IsValidDBValue(val): raise exceptions.InputException('Invalid DB value %r' % val) self.duplicate_update_cols = duplicate_update_cols or [ ] # For REPLACE-ish self.use_clauses = [] self.join_clauses, self.join_args = [], [] self.where_conds, self.where_args = [], [] self.having_conds, self.having_args = [], [] self.group_by_terms, self.group_by_args = [], [] self.order_by_terms, self.order_by_args = [], [] self.limit, self.offset = None, None
def GetRequester(self, metadata): """Return the email address of the signed in user or None.""" # When running on localhost, allow request to specify test account. if TEST_ACCOUNT_HEADER in metadata: if not settings.local_mode: raise exceptions.InputException( 'x-test-account only accepted in local_mode') test_account = metadata[TEST_ACCOUNT_HEADER] if not test_account.endswith('@example.com'): raise exceptions.InputException( 'test_account must end with @example.com') logging.info('Using test_account: %r' % test_account) return test_account # Cookie-based auth user = users.get_current_user() if user: logging.info('Using cookie user: %r', user.email()) return user.email() # Oauth try: user = oauth.get_current_user(framework_constants.OAUTH_SCOPE) if user: logging.info('Oauth requester %s', user.email()) return user.email() except oauth.Error as ex: logging.info('Got oauth error: %r', ex) return None
def ConvertIssueApprovalsTemplate(self, mc, request): """Update an issue's existing approvals structure to match the one of the given template.""" if not request.issue_ref.local_id or not request.issue_ref.project_name: raise exceptions.InputException('Param `issue_ref.local_id` empty') if not request.template_name: raise exceptions.InputException('Param `template_name` empty') project, issue, config = self._GetProjectIssueAndConfig( mc, request.issue_ref, use_cache=False) with work_env.WorkEnv(mc, self.services) as we: we.ConvertIssueApprovalsTemplate(config, issue, request.template_name, request.comment_content, send_email=request.send_email) related_refs = we.GetRelatedIssueRefs([issue]) with mc.profiler.Phase('making user views'): users_involved_in_issue = tracker_bizobj.UsersInvolvedInIssues( [issue]) users_by_id = framework_views.MakeAllUserViews( mc.cnxn, self.services.user, users_involved_in_issue) framework_views.RevealAllEmailsToMembers(mc.auth, project, users_by_id) with mc.profiler.Phase('converting to response objects'): response = issues_pb2.ConvertIssueApprovalsTemplateResponse() response.issue.CopyFrom( converters.ConvertIssue(issue, users_by_id, related_refs, config)) return response
def InviteLinkedParent(self, cnxn, parent_id, child_id): """Child stores an invite for the proposed parent user to consider.""" if not parent_id: raise exceptions.InputException('Parent account is missing') if not child_id: raise exceptions.InputException('Child account is missing') self._AssertNotAlreadyLinked(cnxn, parent_id, child_id) self.linkedaccountinvite_tbl.InsertRow(cnxn, parent_id=parent_id, child_id=child_id)
def UnlinkAccounts(self, cnxn, parent_id, child_id): """Delete a linked-account relationship.""" if not parent_id: raise exceptions.InputException('Parent account is missing') if not child_id: raise exceptions.InputException('Child account is missing') self.linkedaccount_tbl.Delete(cnxn, parent_id=parent_id, child_id=child_id) self.user_2lc.InvalidateKeys(cnxn, [parent_id, child_id])
def IssueSnapshot(self, mc, request): """Fetch IssueSnapshot counts for charting.""" warnings = [] if not request.timestamp: raise exceptions.InputException('Param `timestamp` required.') if not request.project_name: raise exceptions.InputException('Param `project_name` required.') if request.group_by == 'label' and not request.label_prefix: raise exceptions.InputException('Param `label_prefix` required.') if request.canned_query: canned_query = savedqueries_helpers.SavedQueryIDToCond( mc.cnxn, self.services.features, request.canned_query) # TODO(jrobbins): support linked accounts me_user_ids. canned_query, warnings = searchpipeline.ReplaceKeywordsWithUserIDs( [mc.auth.user_id], canned_query) else: canned_query = None if request.query: query, warnings = searchpipeline.ReplaceKeywordsWithUserIDs( [mc.auth.user_id], request.query) else: query = None with work_env.WorkEnv(mc, self.services) as we: project = we.GetProjectByName(request.project_name) results, unsupported_fields, limit_reached = we.SnapshotCountsQuery( project, request.timestamp, request.group_by, label_prefix=request.label_prefix, query=query, canned_query=canned_query) if request.group_by == 'owner': # Map user ids to emails. snapshot_counts = [ issues_pb2.IssueSnapshotCount( dimension=self.services.user.GetUser(mc.cnxn, key).email, count=result) for key, result in results.iteritems() ] else: snapshot_counts = [ issues_pb2.IssueSnapshotCount(dimension=key, count=result) for key, result in results.items() ] response = issues_pb2.IssueSnapshotResponse() response.snapshot_count.extend(snapshot_counts) response.unsupported_field.extend(unsupported_fields) response.unsupported_field.extend(warnings) response.search_limit_reached = limit_reached return response
def IngestAttachmentUploads(attachment_uploads): """Ingest protoc AttachmentUpload objects as tuples.""" result = [] for up in attachment_uploads: if not up.filename: raise exceptions.InputException('Missing attachment name') if not up.content: raise exceptions.InputException('Missing attachment content') mimetype = filecontent.GuessContentTypeFromFilename(up.filename) attachment_tuple = (up.filename, up.content, mimetype) result.append(attachment_tuple) return result
def ParsePersonData(self, mr, post_data): """Parse the POST data for a project member. Args: mr: common information parsed from the user's request. post_data: dictionary of lists of values for each HTML form field. Returns: A tuple with user_id, role, extra_perms, and notes. """ if not mr.specified_user_id: raise exceptions.InputException('Field user_id is missing') role = post_data.get('role', '').lower() extra_perms = [] for ep in post_data.getall('extra_perms'): perm = framework_bizobj.CanonicalizeLabel(ep) # Perms with leading underscores are reserved. perm = perm.strip('_') if perm: extra_perms.append(perm) notes = post_data.get('notes', '').strip() ac_exclusion = not post_data.get('ac_include', False) no_expand = not post_data.get('ac_expand', False) return (mr.specified_user_id, role, extra_perms, notes, ac_exclusion, no_expand)
def GatherPageData(self, mr): """Build up a dictionary of data values to use when rendering the page.""" if not mr.project_name: raise exceptions.InputException('No project specified') return { 'http_response_code': httplib.NOT_FOUND, }
def ListReferencedIssues(self, mc, request): """Return the specified issues in a response proto.""" if not request.issue_refs: return issues_pb2.ListReferencedIssuesResponse() for issue_ref in request.issue_refs: if not issue_ref.project_name: raise exceptions.InputException( 'Param `project_name` required.') if not issue_ref.local_id: raise exceptions.InputException('Param `local_id` required.') default_project_name = request.issue_refs[0].project_name ref_tuples = [(ref.project_name, ref.local_id) for ref in request.issue_refs] with work_env.WorkEnv(mc, self.services) as we: open_issues, closed_issues = we.ListReferencedIssues( ref_tuples, default_project_name) all_issues = open_issues + closed_issues all_project_ids = [issue.project_id for issue in all_issues] related_refs = we.GetRelatedIssueRefs(all_issues) configs = we.GetProjectConfigs(all_project_ids) with mc.profiler.Phase('making user views'): users_involved = tracker_bizobj.UsersInvolvedInIssues(all_issues) users_by_id = framework_views.MakeAllUserViews( mc.cnxn, self.services.user, users_involved) framework_views.RevealAllEmailsToMembers(mc.auth, None, users_by_id) with mc.profiler.Phase('converting to response objects'): converted_open_issues = [ converters.ConvertIssue(issue, users_by_id, related_refs, configs[issue.project_id]) for issue in open_issues ] converted_closed_issues = [ converters.ConvertIssue(issue, users_by_id, related_refs, configs[issue.project_id]) for issue in closed_issues ] response = issues_pb2.ListReferencedIssuesResponse( open_refs=converted_open_issues, closed_refs=converted_closed_issues) return response
def ParseRequest(self, request, services, do_user_lookups=True): """Parse tons of useful info from the given request object. Args: request: webapp2 Request object w/ path and query params. services: connections to backend servers including DB. do_user_lookups: Set to False to disable lookups during testing. """ with self.profiler.Phase('basic parsing'): self.request = request self.current_page_url = request.url self.current_page_url_encoded = urllib.quote_plus( self.current_page_url) # Only accept a hostport from the request that looks valid. if not _HOSTPORT_RE.match(request.host): raise exceptions.InputException('request.host looks funny: %r', request.host) logging.info('Request: %s', self.current_page_url) with self.profiler.Phase('path parsing'): (viewed_user_val, self.project_name, self.hotlist_id, self.hotlist_name) = _ParsePathIdentifiers(self.request.path) self.viewed_username = _GetViewedEmail(viewed_user_val, self.cnxn, services) with self.profiler.Phase('qs parsing'): self._ParseQueryParameters() with self.profiler.Phase('overrides parsing'): self._ParseFormOverrides() if not self.project: # It can be already set in unit tests. self._LookupProject(services) if self.project_id and services.config: self.config = services.config.GetProjectConfig( self.cnxn, self.project_id) if do_user_lookups: if self.viewed_username: self._LookupViewedUser(services) self._LookupLoggedInUser(services) # TODO(jrobbins): re-implement HandleLurkerViewingSelf() if not self.hotlist: self._LookupHotlist(services) if self.query is None: self.query = self._CalcDefaultQuery() prod_debug_allowed = self.perms.HasPerm(permissions.VIEW_DEBUG, self.auth.user_id, None) self.debug_enabled = (request.params.get('debug') and (settings.local_mode or prod_debug_allowed)) # temporary option for perf testing on staging instance. if request.params.get('disable_cache'): if settings.local_mode or 'staging' in request.host: self.use_cached_searches = False
def _AssertNotAlreadyLinked(self, cnxn, parent_id, child_id): """Check constraints on our linked account graph.""" # Our linked account graph should be no more than one level deep. parent_is_already_a_child = self.linkedaccount_tbl.Select( cnxn, cols=LINKEDACCOUNT_COLS, child_id=parent_id) if parent_is_already_a_child: raise exceptions.InputException( 'Parent account is already a child') child_is_already_a_parent = self.linkedaccount_tbl.Select( cnxn, cols=LINKEDACCOUNT_COLS, parent_id=child_id) if child_is_already_a_parent: raise exceptions.InputException( 'Child account is already a parent') # A child account can only be linked to one parent. child_is_already_a_child = self.linkedaccount_tbl.Select( cnxn, cols=LINKEDACCOUNT_COLS, child_id=child_id) if child_is_already_a_child: raise exceptions.InputException('Child account is already linked')
def GetIntListParam(self, query_param_name, default_value=None): """Get a list of ints from the URL or default.""" param_list = self.GetListParam(query_param_name) if param_list is None: return default_value try: return [int(p) for p in param_list] except (TypeError, ValueError): raise exceptions.InputException( 'Invalid value for integer list param')
def GetIntParam(self, query_param_name, default_value=None): """Get an integer param from the URL or default.""" value = self.request.params.get(query_param_name) if value is None or value == '': return default_value try: return int(value) except (TypeError, ValueError): raise exceptions.InputException( 'Invalid value for integer param: %r' % value)
def FlagComment(self, mc, request): """Flag or unflag the given comment as spam.""" _project, issue, _config = self._GetProjectIssueAndConfig( mc, request.issue_ref, use_cache=False) with work_env.WorkEnv(mc, self.services) as we: comments = we.ListIssueComments(issue) if request.sequence_num >= len(comments): raise exceptions.InputException('Invalid sequence number.') we.FlagComment(issue, comments[request.sequence_num], request.flag) result = issues_pb2.FlagCommentResponse() return result
def FlagIssues(self, mc, request): """Flag or unflag the given issues as spam.""" if not request.issue_refs: raise exceptions.InputException('Param `issue_refs` empty.') _project, issue_ids, _config = self._GetProjectIssueIDsAndConfig( mc, request.issue_refs) with work_env.WorkEnv(mc, self.services) as we: issues_by_id = we.GetIssuesDict(issue_ids, use_cache=False) we.FlagIssues(list(issues_by_id.values()), request.flag) result = issues_pb2.FlagIssuesResponse() return result
def DeleteAttachment(self, mc, request): """Mark or unmark the given attachment as deleted.""" _project, issue, _config = self._GetProjectIssueAndConfig( mc, request.issue_ref, use_cache=False) with work_env.WorkEnv(mc, self.services) as we: comments = we.ListIssueComments(issue) if request.sequence_num >= len(comments): raise exceptions.InputException('Invalid sequence number.') we.DeleteAttachment(issue, comments[request.sequence_num], request.attachment_id, request.delete) result = issues_pb2.DeleteAttachmentResponse() return result
def ListProjectTemplates(self, mc, request): """Return the specific project's templates.""" if not request.project_name: raise exceptions.InputException('Param `project_name` required.') project = self._GetProject(mc, request) with work_env.WorkEnv(mc, self.services) as we: templates = we.ListProjectTemplates(project) with mc.profiler.Phase('converting to response objects'): response = projects_pb2.ListProjectTemplatesResponse( templates=converters.ConvertTemplates(templates)) return response
def AcceptLinkedChild(self, cnxn, parent_id, child_id): """Parent accepts an invite from a child account.""" if not parent_id: raise exceptions.InputException('Parent account is missing') if not child_id: raise exceptions.InputException('Child account is missing') # Check that the child has previously created an invite for this parent. invite_rows = self.linkedaccountinvite_tbl.Select( cnxn, cols=LINKEDACCOUNTINVITE_COLS, parent_id=parent_id, child_id=child_id) if not invite_rows: raise exceptions.InputException('No such invite') self._AssertNotAlreadyLinked(cnxn, parent_id, child_id) self.linkedaccount_tbl.InsertRow(cnxn, parent_id=parent_id, child_id=child_id) self.linkedaccountinvite_tbl.Delete(cnxn, parent_id=parent_id, child_id=child_id) self.user_2lc.InvalidateKeys(cnxn, [parent_id, child_id])
def GatherPageData(self, mr): """Build up a dictionary of data values to use when rendering the page. Args: mr: commonly used info parsed from the request. Returns: A dict of values used by EZT for rendering the page. """ artifact_name = mr.GetParam('name') if not artifact_name: raise exceptions.InputException() # someone forged a link artifact_detail_url = '/p/%s/issues/detail?id=%s' % ( mr.project_name, mr.continue_issue_id) return { 'artifact_name': artifact_name, 'artifact_detail_url': artifact_detail_url, }
def _ParsePathIdentifiers(path): """Parse out the workspace being requested (if any). Args: path: A string beginning with the request's path info. Returns: (viewed_user_val, project_name). """ viewed_user_val = None project_name = None hotlist_id = None hotlist_name = None # Strip off any query params split_path = path.lstrip('/').split('?')[0].split('/') if len(split_path) >= 2: if split_path[0] == 'p': project_name = split_path[1] if split_path[0] == 'u': viewed_user_val = urllib.unquote(split_path[1]) if len(split_path) >= 4 and split_path[2] == 'hotlists': try: hotlist_id = int( urllib.unquote(split_path[3].split('.')[0])) except ValueError: raw_last_path = (split_path[3][:-3] if split_path[3].endswith('.do') else split_path[3]) last_path = urllib.unquote(raw_last_path) match = framework_bizobj.RE_HOTLIST_NAME.match(last_path) if not match: raise exceptions.InputException( 'Could not parse hotlist id or name') else: hotlist_name = last_path.lower() if split_path[0] == 'g': viewed_user_val = urllib.unquote(split_path[1]) return viewed_user_val, project_name, hotlist_id, hotlist_name
def GatherPageData(self, mr): """Build up a dictionary of data values to use when rendering the page.""" # We are not actually in /p/PROJECTNAME, so mr.project_name is None. # Putting the ProjectMoved page inside a moved project would make # the redirect logic much more complicated. if not mr.specified_project: raise exceptions.InputException('No project specified') project = self.services.project.GetProjectByName( mr.cnxn, mr.specified_project) if not project: self.abort(404, 'project not found') if not project.moved_to: # Only show this page for projects that are actually moved. # Don't allow hackers to construct misleading links to this servlet. logging.info( 'attempt to view ProjectMoved for non-moved project: %s', mr.specified_project) self.abort(400, 'This project has not been moved') if framework_bizobj.RE_PROJECT_NAME.match(project.moved_to): moved_to_url = framework_helpers.FormatAbsoluteURL( mr, urls.SUMMARY, include_project=True, project_name=project.moved_to) elif (project.moved_to.startswith('https://') or project.moved_to.startswith('http://')): moved_to_url = project.moved_to else: # Prevent users from using javascript: or any other tricky URL scheme. moved_to_url = '#invalid-destination-url' return { 'project_name': mr.specified_project, 'moved_to_url': moved_to_url, }
def GetParam(self, query_param_name, default_value=None, antitamper_re=None): """Get a query parameter from the URL as a utf8 string.""" value = self.request.params.get(query_param_name) assert value is None or isinstance(value, six.text_type) using_default = value is None if using_default: value = default_value if antitamper_re and not antitamper_re.match(value): if using_default: logging.error( 'Default value fails antitamper for %s field: %s', query_param_name, value) else: logging.info('User seems to have tampered with %s field: %s', query_param_name, value) raise exceptions.InputException() return value
def BulkUpdateApprovals(self, mc, request): """Update multiple issues' approval and return the updated issue_refs.""" if not request.issue_refs: raise exceptions.InputException('Param `issue_refs` empty.') project, issue_ids, config = self._GetProjectIssueIDsAndConfig( mc, request.issue_refs) approval_fd = tracker_bizobj.FindFieldDef(request.field_ref.field_name, config) if not approval_fd: raise exceptions.NoSuchFieldDefException() if request.HasField('approval_delta'): approval_delta = converters.IngestApprovalDelta( mc.cnxn, self.services.user, request.approval_delta, mc.auth.user_id, config) else: approval_delta = tracker_pb2.ApprovalDelta() # No bulk adding approval attachments for now. with work_env.WorkEnv(mc, self.services, phase='updating approvals') as we: updated_issue_ids = we.BulkUpdateIssueApprovals( issue_ids, approval_fd.field_id, project, approval_delta, request.comment_content, send_email=request.send_email) with mc.profiler.Phase('converting to response objects'): issue_ref_pairs = we.GetIssueRefs(updated_issue_ids) issue_refs = [ converters.ConvertIssueRef(pair) for pair in issue_ref_pairs.values() ] response = issues_pb2.BulkUpdateApprovalsResponse( issue_refs=issue_refs) return response
def IngestFieldValues(cnxn, user_service, field_values, config, phases=None): """Ingest a list of protoc FieldValues and create protorpc FieldValues. Args: cnxn: connection to the DB. user_service: interface to user data storage. field_values: a list of protoc FieldValue used by the API. config: ProjectIssueConfig for this field_value's project. phases: a list of the issue's protorpc Phases. Returns: A protorpc FieldValue object. """ fds_by_name = {fd.field_name.lower(): fd for fd in config.field_defs} phases_by_name = {phase.name: phase.phase_id for phase in phases or []} ingested_fvs = [] for fv in field_values: fd = fds_by_name.get(fv.field_ref.field_name.lower()) if fd: if not fv.value: logging.info('Ignoring blank field value: %r', fv) continue ingested_fv = field_helpers.ParseOneFieldValue( cnxn, user_service, fd, fv.value) if not ingested_fv: raise exceptions.InputException( 'Unparsable value for field %s' % fv.field_ref.field_name) if ingested_fv.user_id == field_helpers.INVALID_USER_ID: raise exceptions.NoSuchUserException() if fd.is_phase_field: ingested_fv.phase_id = phases_by_name.get( fv.phase_ref.phase_name) ingested_fvs.append(ingested_fv) return ingested_fvs
def __init__(self, request, services, cnxn=None): requester_object = (endpoints.get_current_user() or oauth.get_current_user( framework_constants.OAUTH_SCOPE)) requester = requester_object.email().lower() super(MonorailApiRequest, self).__init__(services, requester=requester, cnxn=cnxn) self.me_user_id = self.auth.user_id self.viewed_username = None self.viewed_user_auth = None self.issue = None self.granted_perms = set() # query parameters self.params = { 'can': 1, 'start': 0, 'num': tracker_constants.DEFAULT_RESULTS_PER_PAGE, 'q': '', 'sort': '', 'groupby': '', 'projects': [], 'hotlists': [] } self.use_cached_searches = True self.mode = None if hasattr(request, 'projectId'): self.project_name = request.projectId with work_env.WorkEnv(self, services) as we: self.project = we.GetProjectByName(self.project_name) self.params['projects'].append(self.project_name) self.config = we.GetProjectConfig(self.project_id) if hasattr(request, 'additionalProject'): self.params['projects'].extend(request.additionalProject) self.params['projects'] = list(set( self.params['projects'])) self.LookupLoggedInUserPerms(self.project) if hasattr(request, 'projectId'): with work_env.WorkEnv(self, services) as we: if hasattr(request, 'issueId'): self.issue = we.GetIssueByLocalID(self.project_id, request.issueId, use_cache=False) self.granted_perms = tracker_bizobj.GetGrantedPerms( self.issue, self.auth.effective_ids, self.config) if hasattr(request, 'userId'): self.viewed_username = request.userId.lower() if self.viewed_username == 'me': self.viewed_username = requester self.viewed_user_auth = authdata.AuthData.FromEmail( self.cnxn, self.viewed_username, services) elif hasattr(request, 'groupName'): self.viewed_username = request.groupName.lower() try: self.viewed_user_auth = authdata.AuthData.FromEmail( self.cnxn, self.viewed_username, services) except exceptions.NoSuchUserException: self.viewed_user_auth = None # Build q. if hasattr(request, 'q') and request.q: self.params['q'] = request.q if hasattr(request, 'publishedMax') and request.publishedMax: self.params['q'] += ' opened<=%d' % request.publishedMax if hasattr(request, 'publishedMin') and request.publishedMin: self.params['q'] += ' opened>=%d' % request.publishedMin if hasattr(request, 'updatedMax') and request.updatedMax: self.params['q'] += ' modified<=%d' % request.updatedMax if hasattr(request, 'updatedMin') and request.updatedMin: self.params['q'] += ' modified>=%d' % request.updatedMin if hasattr(request, 'owner') and request.owner: self.params['q'] += ' owner:%s' % request.owner if hasattr(request, 'status') and request.status: self.params['q'] += ' status:%s' % request.status if hasattr(request, 'label') and request.label: self.params['q'] += ' label:%s' % request.label if hasattr(request, 'can') and request.can: if request.can == api_pb2_v1.CannedQuery.all: self.params['can'] = 1 elif request.can == api_pb2_v1.CannedQuery.new: self.params['can'] = 6 elif request.can == api_pb2_v1.CannedQuery.open: self.params['can'] = 2 elif request.can == api_pb2_v1.CannedQuery.owned: self.params['can'] = 3 elif request.can == api_pb2_v1.CannedQuery.reported: self.params['can'] = 4 elif request.can == api_pb2_v1.CannedQuery.starred: self.params['can'] = 5 elif request.can == api_pb2_v1.CannedQuery.to_verify: self.params['can'] = 7 else: # Endpoints should have caught this. raise exceptions.InputException( 'Canned query %s is not supported.', request.can) if hasattr(request, 'startIndex') and request.startIndex: self.params['start'] = request.startIndex if hasattr(request, 'maxResults') and request.maxResults: self.params['num'] = request.maxResults if hasattr(request, 'sort') and request.sort: self.params['sort'] = request.sort self.query_project_names = self.GetParam('projects') self.group_by_spec = self.GetParam('groupby') self.group_by_spec = ' '.join( ParseColSpec(self.group_by_spec, ignore=tracker_constants.NOT_USED_IN_GRID_AXES)) self.sort_spec = self.GetParam('sort') self.sort_spec = ' '.join(ParseColSpec(self.sort_spec)) self.query = self.GetParam('q') self.can = self.GetParam('can') self.start = self.GetParam('start') self.num = self.GetParam('num')
def _ParseOneRule( cnxn, predicate, action_type, action_value, user_service, rule_num, error_list): """Parse one FilterRule based on the action type.""" if action_type == 'default_status': status = framework_bizobj.CanonicalizeLabel(action_value) rule = MakeRule(predicate, default_status=status) elif action_type == 'default_owner': if action_value: try: user_id = user_service.LookupUserID(cnxn, action_value) except exceptions.NoSuchUserException: user_id = framework_constants.NO_USER_SPECIFIED error_list.append( 'Rule %d: No such user: %s' % (rule_num, action_value)) else: user_id = framework_constants.NO_USER_SPECIFIED rule = MakeRule(predicate, default_owner_id=user_id) elif action_type == 'add_ccs': cc_ids = [] for email in re.split('[,;\s]+', action_value): if not email.strip(): continue try: user_id = user_service.LookupUserID( cnxn, email.strip(), autocreate=True) cc_ids.append(user_id) except exceptions.NoSuchUserException: error_list.append( 'Rule %d: No such user: %s' % (rule_num, email.strip())) rule = MakeRule(predicate, add_cc_ids=cc_ids) elif action_type == 'add_labels': add_labels = framework_constants.IDENTIFIER_RE.findall(action_value) rule = MakeRule(predicate, add_labels=add_labels) elif action_type == 'also_notify': add_notify = [] for addr in re.split('[,;\s]+', action_value): if validate.IsValidEmail(addr.strip()): add_notify.append(addr.strip()) else: error_list.append( 'Rule %d: Invalid email address: %s' % (rule_num, addr.strip())) rule = MakeRule(predicate, add_notify=add_notify) elif action_type == 'warning': rule = MakeRule(predicate, warning=action_value) elif action_type == 'error': rule = MakeRule(predicate, error=action_value) else: logging.info('unexpected action type, probably tampering:%r', action_type) raise exceptions.InputException() return rule
def GatherPageData(self, mr): """Build up a dictionary of data values to use when rendering the page. Args: mr: commonly used info parsed from the request. Returns: Dict of values used by EZT for rendering the page. """ with mr.profiler.Phase('getting issues'): if not mr.local_id_list: raise exceptions.InputException() requested_issues = self.services.issue.GetIssuesByLocalIDs( mr.cnxn, mr.project_id, sorted(mr.local_id_list)) with mr.profiler.Phase('filtering issues'): # TODO(jrobbins): filter out issues that the user cannot edit and # provide that as feedback rather than just siliently ignoring them. open_issues, closed_issues = ( tracker_helpers.GetAllowedOpenedAndClosedIssues( mr, [issue.issue_id for issue in requested_issues], self.services)) issues = open_issues + closed_issues if not issues: self.abort(404, 'no issues found') config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id) type_label_set = { lab.lower() for lab in issues[0].labels if lab.lower().startswith('type-') } for issue in issues[1:]: new_type_set = { lab.lower() for lab in issue.labels if lab.lower().startswith('type-') } type_label_set &= new_type_set issue_phases = list( itertools.chain.from_iterable(issue.phases for issue in issues)) field_views = tracker_views.MakeAllFieldValueViews(config, type_label_set, [], [], {}, phases=issue_phases) # Explicitly set all field views to not required. We do not want to force # users to have to set it for issues missing required fields. # See https://bugs.chromium.org/p/monorail/issues/detail?id=500 for more # details. for fv in field_views: fv.field_def.is_required_bool = None with mr.profiler.Phase('making issue proxies'): issue_views = [ template_helpers.EZTItem( local_id=issue.local_id, summary=issue.summary, closed=ezt.boolean(issue in closed_issues)) for issue in issues ] num_seconds = (int(len(issue_views) * self._SECONDS_PER_UPDATE) + self._SECONDS_OVERHEAD) page_perms = self.MakePagePerms(mr, None, permissions.CREATE_ISSUE, permissions.DELETE_ISSUE) return { 'issue_tab_mode': 'issueBulkEdit', 'issues': issue_views, 'local_ids_str': ','.join([str(issue.local_id) for issue in issues]), 'num_issues': len(issue_views), 'show_progress': ezt.boolean(num_seconds > self._SLOWNESS_THRESHOLD), 'num_seconds': num_seconds, 'initial_blocked_on': '', 'initial_blocking': '', 'initial_comment': '', 'initial_status': '', 'initial_owner': '', 'initial_merge_into': '', 'initial_cc': '', 'initial_components': '', 'labels': [], 'fields': field_views, 'restrict_to_known': ezt.boolean(config.restrict_to_known), 'page_perms': page_perms, 'statuses_offer_merge': config.statuses_offer_merge, 'issue_phase_names': list({phase.name.lower() for phase in issue_phases}), }