Coverage for webapp/search/views.py: 81%
84 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
1import logging
2from flask import (
3 Blueprint,
4 jsonify,
5 request,
6 render_template,
7)
8from redis_cache.cache_utility import redis_cache
9from webapp.config import SEARCH_FIELDS
10from webapp.search.logic import (
11 search_docs,
12 search_topics,
13 search_charms,
14 search_bundles,
15)
16from webapp.observability.utils import trace_function
17from webapp.store_api import publisher_gateway
20logger = logging.getLogger(__name__)
22search = Blueprint(
23 "search", __name__, template_folder="/templates", static_folder="/static"
24)
27@trace_function
28@search.route("/all-search")
29def all_search():
30 return render_template("all-search.html")
33@trace_function
34@search.route("/all-search.json")
35def all_search_json():
36 params = request.args
37 term = params.get("q")
38 limit = int(params.get("limit", 5))
40 if not term:
41 return {"error": "No search term provided"}
42 key = ("all-search-json", {"q": term, "limit": limit})
43 result = redis_cache.get(key, expected_type=dict)
44 if result:
45 return jsonify(result)
46 result = {
47 "charms": search_charms(term)[:limit],
48 "bundles": search_bundles(term)[:limit],
49 "docs": search_docs(term)[:limit],
50 "topics": search_topics(term, 1, False)[:limit],
51 }
52 redis_cache.set(key, result, ttl=600)
53 return jsonify(result)
56@trace_function
57@search.route("/all-charms")
58@search.route("/all-bundles")
59def all_charms() -> dict:
60 query = request.args.get("q", "")
61 page = int(request.args.get("page", 1))
62 limit = int(request.args.get("limit", 50))
63 key = (f"{request.path}", {"q": query, "pg": page})
64 cached_page = redis_cache.get(key, expected_type=dict)
65 if cached_page:
66 return jsonify(cached_page)
67 packages = publisher_gateway.find(query, fields=SEARCH_FIELDS)
68 package_type = request.path[1:-1].split("-")[1]
69 result = [
70 package
71 for package in packages["results"]
72 if package["type"] == package_type
73 ]
74 start = (page - 1) * limit
75 end = start + limit
76 result = {f"{package_type}s": result[start:end]}
77 redis_cache.set(key, result, ttl=600)
78 return jsonify(result)
81@trace_function
82@search.route("/all-docs")
83def all_docs():
84 search_term = request.args.get("q")
85 limit = int(request.args.get("limit", 50))
87 docs = search_docs(search_term)[:limit]
89 return jsonify({"docs": docs})
92@trace_function
93@search.route("/all-topics")
94def all_topics():
95 search_term = request.args.get("q")
96 page = int(request.args.get("page", 1))
97 limit = int(request.args.get("limit", 50))
99 key = ("all-topics", {"q": search_term, "pg": page})
100 topics = redis_cache.get(key, expected_type=dict)
101 if topics:
102 return jsonify(topics)
104 all_topics = search_topics(search_term, page, True)[:limit]
105 total_pages = -(len(all_topics) // -limit)
106 start = (page - 1) * limit
107 end = start + limit
108 topics = {"topics": all_topics[start:end], "total_pages": total_pages}
109 redis_cache.set(key, topics, ttl=600)
110 return jsonify(topics)
113@search.route("/validate-charm")
114def validate_charm():
115 charm_name = request.args.get("name", "").strip()
117 if not charm_name:
118 return jsonify({"exists": False, "error": "No charm name provided"})
120 try:
121 charm = publisher_gateway.get_item_details(charm_name)
123 if charm and "name" in charm:
124 return jsonify({"exists": True, "name": charm["name"]})
126 return jsonify({"exists": False})
127 except Exception as e:
128 logger.exception(f"Failed to validate charm: {e}")
129 return jsonify({"exists": False, "error": "Network or API error"})