Open the record a search result found, not the list it sits in

A NOTICE result linked to /notifications, so clicking the row search had
already identified dropped the reader on the list page to find it again by
hand. The id was in the payload the whole time; only the URL ignored it.
`/notifications/<id>` is the existing notification-detail route.

The subnet searcher had the same defect two functions away: an IP search
resolves which subnet the address falls inside, then linked to /network, the
hub. It now links to /networks/<id>, the subnet detail route.

Every other search result type already carried its id. These were the only
two that did not.
This commit is contained in:
cproudlock
2026-08-24 14:05:05 -04:00
parent adc7ef43f9
commit eecb0595dd
2 changed files with 74 additions and 2 deletions

View File

@@ -658,7 +658,10 @@ def _search_subnets(query):
'id': subnet.subnetid, 'id': subnet.subnetid,
'title': f'{subnet.name} ({subnet.cidr})', 'title': f'{subnet.name} ({subnet.cidr})',
'subtitle': subnet.description or subnet.subnettype, 'subtitle': subnet.description or subnet.subnettype,
'url': f'/network', # the matched subnet, not the hub: searching an IP
# already identified WHICH subnet, so landing on the
# hub makes the reader find it again by hand
'url': f'/networks/{subnet.subnetid}',
'relevance': 70 'relevance': 70
}) })
except ValueError: except ValueError:
@@ -779,7 +782,9 @@ def _search_notifications(query, search_term):
'id': notif.notificationid, 'id': notif.notificationid,
'title': notif.title, 'title': notif.title,
'subtitle': notif.notificationtype.typename if notif.notificationtype else None, 'subtitle': notif.notificationtype.typename if notif.notificationtype else None,
'url': f'/notifications', # the notification itself, not the list it sits in: the
# `notification-detail` route renders one by id
'url': f'/notifications/{notif.notificationid}',
'relevance': min(int(base_relevance), 100), 'relevance': min(int(base_relevance), 100),
'ticketnumber': notif.ticketnumber, 'ticketnumber': notif.ticketnumber,
'iscurrent': notif.is_current 'iscurrent': notif.is_current

View File

@@ -199,3 +199,70 @@ def test_a_machine_is_found_by_its_maintenance_reference(client, taggedmachine):
type - a field the UI collects and search could not find.""" type - a field the UI collects and search could not find."""
rows = [r for r in search(client, 'ZZMAINT888') if r['title'] == 'ZZMACH01'] rows = [r for r in search(client, 'ZZMAINT888') if r['title'] == 'ZZMACH01']
assert rows, 'a maintenance reference matched nothing' assert rows, 'a maintenance reference matched nothing'
# --- a search result must land on the record, not its list -------------------
#
# A result carries an id, so sending the reader to the plugin's list page makes
# them find by hand the row search had already identified. Both cases below
# shipped that way: the id was in the payload and the URL ignored it.
@pytest.fixture
def noticerow(db):
"""One notification with a distinctive body and its own type."""
from plugins.notifications.models import Notification, NotificationType
notice = NotificationType(typename='Notice', typecolor='#17a2b8',
isactive=True)
db.session.add(notice)
db.session.flush()
row = Notification(
notificationtypeid=notice.notificationtypeid,
notification='ZZNOTICE7788 press brake down for repair',
ticketnumber='INC7788',
isactive=True,
)
db.session.add(row)
db.session.commit()
return row.notificationid
def test_a_notice_result_links_to_the_notice(client, noticerow):
"""Clicking a NOTICE opens that notification, not /notifications.
`/notifications/<id>` is the `notification-detail` route; the list page
cannot show which of its rows the search matched.
"""
rows = [r for r in search(client, 'ZZNOTICE7788')
if r['type'] == 'notification']
assert len(rows) == 1
assert rows[0]['id'] == noticerow
assert rows[0]['url'] == f'/notifications/{noticerow}'
def test_a_notice_found_by_ticket_number_links_the_same_way(client, noticerow):
"""The ticket number is the strongest match and must land as precisely."""
rows = [r for r in search(client, 'INC7788')
if r['type'] == 'notification']
assert len(rows) == 1
assert rows[0]['url'] == f'/notifications/{noticerow}'
@pytest.fixture
def subnetrow(db):
"""A subnet an IP search can fall inside."""
from plugins.network.models import Subnet
row = Subnet(cidr='10.77.88.0/24', name='ZZ Test subnet', isactive=True)
db.session.add(row)
db.session.commit()
return row.subnetid
def test_an_ip_search_links_to_the_matched_subnet(client, subnetrow):
"""Searching an IP already picked WHICH subnet; land on it.
The detail route is `/networks/<id>` (plural) - `/network` is the hub.
"""
rows = [r for r in search(client, '10.77.88.42') if r['type'] == 'subnet']
assert len(rows) == 1
assert rows[0]['id'] == subnetrow
assert rows[0]['url'] == f'/networks/{subnetrow}'