Coverage for src/mcp_atlassian/confluence/search.py: 93%

1"""Module for Confluence search operations."""

2

3 import logging

4

5 import requests

6

7 from ..document_types import Document

8 from .client import ConfluenceClient

9

10 logger = logging.getLogger("mcp-atlassian")

11

12

13 class SearchMixin(ConfluenceClient):

14 """Mixin for Confluence search operations."""

15

16 def search(self, cql: str, limit: int = 10) -> list[Document]:

17 """

18 Search content using Confluence Query Language (CQL).

19

20 Args:

21 cql: Confluence Query Language string

22 limit: Maximum number of results to return

23

24 Returns:

25 List of Document objects containing search results

26 """

27 try:

28 results = self.confluence.cql(cql=cql, limit=limit)

29 documents = []

30

31 for result in results.get("results", []):

32 content = result.get("content", {})

33 if content.get("type") == "page":

34 metadata = {

35 "page_id": content["id"],

36 "title": result["title"],

37 "space": result.get("resultGlobalContainer", {}).get("title"),

38 "url": f"{self.config.url}{result['url']}",

39 "last_modified": result.get("lastModified"),

40 "type": content["type"],

41 }

42

43 # Use the excerpt as page_content since it's already a good summary

44 documents.append(

45 Document(

46 page_content=result.get("excerpt", ""), metadata=metadata

47 )

48 )

49

50 return documents

51 except KeyError as e:

52 logger.error(f"Missing key in search results: {str(e)}")

53 return []

54 except requests.RequestException as e:

55 logger.error(f"Network error during search: {str(e)}")

56 return []

57 except (ValueError, TypeError) as e:

58 logger.error(f"Error processing search results: {str(e)}")

59 return []

60 except Exception as e: # noqa: BLE001 - Intentional fallback with logging

61 logger.error(f"Unexpected error during search: {str(e)}")

62 logger.debug("Full exception details for search:", exc_info=True)

63 return []