ADR-013 Phase 3: search routes via get_asset_presentation, not a hardcoded map

Global-search rows built the plugin detail URL from a hardcoded url_map of
plugin routes in core. Now core prefers a plugin's declared
get_asset_presentation route (ADR-010), substituting the core assetid via the
plugin's by-asset resolver; types that have not declared fall back to the legacy
id-keyed map, so nothing breaks. Measuring tools (which declare the route) link
through it now; machines/PCs/printers/network migrate off the hardcode as they
add a by-asset route + declaration. Presentation map is collected once per
search (cached on flask.g). 2 consumer tests; 26 search tests green.
This commit is contained in:
cproudlock
2026-07-18 22:56:05 -04:00
parent b669561421
commit 8a2f984393
2 changed files with 92 additions and 9 deletions

View File

@@ -106,6 +106,32 @@ def _classify_query(query, integrations):
}
def _asset_presentation_map():
"""assettype -> route from enabled plugins' get_asset_presentation (ADR-010).
Lets core route a plugin-owned asset type in search rows without hardcoding
the route. Cached per request (built once per search, not per result).
"""
from flask import g
cached = getattr(g, '_asset_presentation_map', None)
if cached is not None:
return cached
result = {}
pm = current_app.extensions.get('plugin_manager')
if pm:
for plugin_name, plugin in pm.get_all_plugins().items():
if not pm.registry.is_enabled(plugin_name):
continue
try:
for entry in plugin.get_asset_presentation() or []:
if entry.get('assettype') and entry.get('route'):
result[entry['assettype']] = entry['route']
except Exception:
continue
g._asset_presentation_map = result
return result
def _get_asset_result(asset, query, relevance=None):
"""Build a search result dict from an Asset object."""
asset_type_name = asset.assettype.assettype if asset.assettype else 'asset'
@@ -122,14 +148,24 @@ def _get_asset_result(asset, query, relevance=None):
elif asset_type_name == 'measuring_tool' and hasattr(asset, 'measuringtool') and asset.measuringtool:
plugin_id = asset.measuringtool.measuringtoolid
url_map = {
'machine': f"/machines/{plugin_id}",
'computer': f"/pcs/{plugin_id}",
'network_device': f"/network/{plugin_id}",
'printer': f"/printers/{plugin_id}",
'measuring_tool': f"/measuringtools/{plugin_id}",
}
url = url_map.get(asset_type_name, f"/assets/{asset.assetid}")
# Prefer a plugin's declared get_asset_presentation route (ADR-010): core
# stops hardcoding the plugin's route as each plugin declares one. Those
# routes take the core assetid (via a by-asset resolver). Types that have
# not declared fall back to the legacy id-keyed map below.
presentation = _asset_presentation_map()
if asset_type_name in presentation:
url = presentation[asset_type_name].replace('{assetid}', str(asset.assetid))
result_id = asset.assetid
else:
url_map = {
'machine': f"/machines/{plugin_id}",
'computer': f"/pcs/{plugin_id}",
'network_device': f"/network/{plugin_id}",
'printer': f"/printers/{plugin_id}",
'measuring_tool': f"/measuringtools/{plugin_id}",
}
url = url_map.get(asset_type_name, f"/assets/{asset.assetid}")
result_id = plugin_id
display_name = asset.display_name
subtitle = None
@@ -143,7 +179,7 @@ def _get_asset_result(asset, query, relevance=None):
return {
'type': asset_type_name,
'id': plugin_id,
'id': result_id,
'title': display_name,
'subtitle': subtitle,
'location': location_name,