blob: 6ba18ad20c4334913cf7f21d30e2a795487d700c [file]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is govered by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
"""JSON feeds for issue autocomplete options.
These are split into distinct endpoints to reduce the payload size
for large projects.
"""
import logging
from third_party import ezt
from framework import authdata
from framework import framework_bizobj
from framework import framework_helpers
from framework import framework_views
from framework import jsonfeed
from framework import permissions
from project import project_helpers
from tracker import field_helpers
from tracker import tracker_helpers
from tracker import tracker_views
class IssueStatusLabelOptionsJSON(jsonfeed.JsonFeed):
"""JSON data describing all issue statuses and labels."""
def HandleRequest(self, mr):
"""Provide the UI with info used in auto-completion.
Args:
mr: common information parsed from the HTTP request.
Returns:
Results dictionary in JSON format
"""
# Issue options data can be cached separately in each user's browser. When
# the project changes, a new cached_content_timestamp is set and it will
# cause new requests to use a new URL.
self.SetCacheHeaders(self.response)
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
open_statuses, closed_statuses = GetStatusOptions(config)
components = GetComponentOptions(config)
custom_permissions = permissions.GetCustomPermissions(mr.project)
labels = tracker_helpers.GetLabelOptions(config, custom_permissions)
exclusive_prefixes = [
prefix.lower()
for prefix in config.exclusive_label_prefixes
]
hotlists = GetHotlistOptions(mr, self.services)
return {
'open': open_statuses,
'closed': closed_statuses,
'statuses_offer_merge': config.statuses_offer_merge,
'components': components,
'labels': labels,
'excl_prefixes': exclusive_prefixes,
'strict': ezt.boolean(config.restrict_to_known),
'custom_permissions': custom_permissions,
'hotlists': hotlists,
}
class IssueMembersOptionsJSON(jsonfeed.JsonFeed):
"""JSON data describing all issue member & custom field options."""
def HandleRequest(self, mr):
"""Provide the UI with info used in auto-completion.
Args:
mr: common information parsed from the HTTP request.
Returns:
Results dictionary in JSON format
"""
# Issue options data can be cached separately in each user's browser. When
# the project changes, a new cached_content_timestamp is set and it will
# cause new requests to use a new URL.
self.SetCacheHeaders(self.response)
config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
member_views = tracker_helpers.GetVisibleMembers(
mr, mr.project, self.services).values()
member_emails = sorted(set(uv.email for uv in member_views))
group_emails = sorted(set(uv.email for uv in member_views if uv.is_group))
fields = GetFieldOptions(mr, self.services, config, member_views,
member_emails)
return {
'fields': fields,
'members': member_emails,
'group_emails': group_emails,
}
def GetStatusOptions(config):
open_statuses = []
closed_statuses = []
for wks in config.well_known_statuses:
if not wks.deprecated:
item = dict(name=wks.status, doc=wks.status_docstring)
if wks.means_open:
open_statuses.append(item)
else:
closed_statuses.append(item)
return open_statuses, closed_statuses
def GetComponentOptions(config):
"""Prepares component options for autocomplete."""
# TODO(jrobbins): restrictions on component definitions?
return [
{'name': cd.path, 'doc': cd.docstring}
for cd in config.component_defs if not cd.deprecated
]
def GetHotlistOptions(mr, services):
"""Fetches Hotlist options for autocomplete."""
hotlist_pbs = services.features.GetHotlistsByUserID(
mr.cnxn, mr.auth.user_id)
seen = set()
ambiguous_names = set()
for hpb in hotlist_pbs:
if hpb.name in seen:
ambiguous_names.add(hpb.name)
seen.add(hpb.name)
hotlists = list()
ambiguous_owners_ids = {hpb.owner_ids[0] for hpb in hotlist_pbs
if hpb.name in ambiguous_names}
ambiguous_owners_ids_to_emails = services.user.LookupUserEmails(
mr.cnxn, ambiguous_owners_ids)
for hpb in hotlist_pbs:
if hpb.name in ambiguous_names:
ref_str = ':'.join([ambiguous_owners_ids_to_emails[hpb.owner_ids[0]],
hpb.name])
else:
ref_str = hpb.name
hotlists.append({'ref_str': ref_str, 'summary': hpb.summary})
return hotlists
def GetFieldOptions(mr, services, config, visible_member_views,
visible_member_email_list):
"""Fetches custom field options for autocomplete."""
user_indexes = {email: idx
for idx, email in enumerate(visible_member_email_list)}
# TODO(jrobbins): omit fields that they don't have permission to view.
fields = []
for fd in config.field_defs:
if fd.is_deleted:
continue
qualified_users = []
if fd.needs_perm:
qualified_users = field_helpers.FilterValidUserFieldValues(
mr, mr.project, services, fd, visible_member_views)
fdv = tracker_views.FieldDefView(fd, config)
fields.append(dict(
field_name=fdv.field_name, field_type=fdv.field_type,
field_id=fdv.field_id, needs_perm=fdv.needs_perm,
is_required=fdv.is_required, is_multivalued=fdv.is_multivalued,
choices=[dict(name=c.name, doc=c.docstring) for c in fdv.choices],
user_indexes=sorted(user_indexes[uv.email] for uv in qualified_users),
docstring=fdv.docstring))
return sorted(fields, key=lambda field: field['field_name'])