Coverage for src/lexigram/admin/controllers/command_palette.py: 0%

30 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 23:18 +0800

1"""Controller for the command palette endpoint.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from starlette.requests import Request 

8from starlette.responses import JSONResponse 

9 

10from lexigram.admin.services.search_service import SearchService 

11from lexigram.di.decorators import inject 

12from lexigram.logging import get_logger 

13 

14logger = get_logger(__name__) 

15 

16_STATIC_COMMANDS: list[dict[str, Any]] = [ 

17 {"label": "Go to Dashboard", "href": "/admin/", "icon": "home", "shortcut": "G D"}, 

18 { 

19 "label": "Manage Users", 

20 "href": "/admin/users", 

21 "icon": "users", 

22 "shortcut": "G U", 

23 }, 

24 { 

25 "label": "Toggle Dark Mode", 

26 "action": "darkMode = !darkMode", 

27 "icon": "moon", 

28 "shortcut": "T D", 

29 }, 

30 {"label": "Settings", "href": "#", "icon": "settings", "shortcut": ","}, 

31] 

32 

33_MIN_QUERY_LENGTH = 2 

34 

35 

36@inject 

37class CommandPaletteController: 

38 """Handles the command palette search endpoint. 

39 

40 Returns JSON commands that the frontend merges with static commands. 

41 """ 

42 

43 def __init__(self, search_service: SearchService) -> None: 

44 self._search_service = search_service 

45 

46 async def search(self, request: Request) -> JSONResponse: 

47 """Handle GET /admin/command-palette?q=...""" 

48 query = (request.query_params.get("q") or "").strip() 

49 commands: list[dict[str, Any]] = [] 

50 

51 # Filter static commands by query 

52 for cmd in _STATIC_COMMANDS: 

53 if not query or query.lower() in cmd["label"].lower(): 

54 commands.append(cmd) 

55 

56 # Dynamic search results from backend 

57 if len(query) >= _MIN_QUERY_LENGTH: 

58 try: 

59 user = getattr(request.state, "user", None) 

60 allowed = await self._search_service.allowed_resources_for(user) 

61 results = await self._search_service.search( 

62 query, allowed_resources=allowed 

63 ) 

64 for r in results.results: 

65 commands.append( 

66 { 

67 "label": f"{r.resource_label}: {r.title}", 

68 "href": r.url, 

69 "icon": "search", 

70 "subtitle": r.subtitle, 

71 } 

72 ) 

73 except Exception: 

74 logger.exception("Command palette search failed for query=%s", query) 

75 

76 return JSONResponse(commands)