Coverage for webapp / endpoints / snaps.py: 65%
66 statements
« prev ^ index » next coverage.py v7.13.1, created at 2025-12-29 22:06 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2025-12-29 22:06 +0000
1import flask
2from flask import make_response
3from flask.json import jsonify
4import json
6import dns.resolver
7import re
9import webapp.helpers as helpers
10from webapp.decorators import login_required, exchange_required
12from canonicalwebteam.store_api.devicegw import DeviceGW
13from canonicalwebteam.store_api.dashboard import Dashboard
15device_gateway = DeviceGW("snap", helpers.api_session)
16dashboard = Dashboard(helpers.api_session)
18FIELDS = [
19 "title",
20 "summary",
21 "description",
22 "license",
23 "contact",
24 "website",
25 "publisher",
26 "media",
27 "download",
28 "version",
29 "created-at",
30 "confinement",
31 "categories",
32 "trending",
33 "unlisted",
34 "links",
35]
36snaps = flask.Blueprint(
37 "snaps",
38 __name__,
39)
42snap_regex = "[a-z0-9-]*[a-z][a-z0-9-]*"
45def _get_snap_link_fields(snap_name):
46 details = device_gateway.get_item_details(
47 snap_name, api_version=2, fields=FIELDS
48 )
49 return {
50 "links": details["snap"].get("links", {}),
51 }
54@snaps.route('/api/<regex("' + snap_regex + '"):snap_name>/verify')
55def dns_verified_status(snap_name):
56 res = {"primary_domain": False, "token": None}
57 context = _get_snap_link_fields(snap_name)
59 primary_domain = None
61 if "website" in context["links"]:
62 primary_domain = context["links"]["website"][0]
64 if primary_domain:
65 token = helpers.get_dns_verification_token(snap_name, primary_domain)
67 domain = re.compile(r"https?://(www\.)?")
68 domain = domain.sub("", primary_domain).strip().strip("/")
70 res["token"] = token
72 try:
73 dns_txt_records = [
74 dns_record.to_text()
75 for dns_record in dns.resolver.resolve(domain, "TXT").rrset
76 ]
78 if f'"SNAPCRAFT_IO_VERIFICATION={token}"' in dns_txt_records:
79 res["primary_domain"] = True
81 except Exception:
82 res["primary_domain"] = False
84 response = make_response(res, 200)
85 response.cache_control.max_age = "3600"
86 return response
89@snaps.route("/api/store/<store_id>/snaps")
90@login_required
91@exchange_required
92def get_store_snaps(store_id):
93 snaps = dashboard.get_store_snaps(flask.session, store_id)
94 store = dashboard.get_store(flask.session, store_id)
95 if "store-whitelist" in store:
96 included_stores = []
97 for item in store["store-whitelist"]:
98 try:
99 store_item = dashboard.get_store(flask.session, item)
100 if store_item:
101 included_stores.append(
102 {
103 "id": store_item["id"],
104 "name": store_item["name"],
105 "userHasAccess": True,
106 }
107 )
108 except Exception:
109 included_stores.append(
110 {
111 "id": item,
112 "name": "Private store",
113 "userHasAccess": False,
114 }
115 )
117 if included_stores:
118 snaps.append({"included-stores": included_stores})
119 return jsonify(snaps)
122@snaps.route("/api/store/<store_id>/snaps", methods=["POST"])
123@login_required
124@exchange_required
125def post_manage_store_snaps(store_id):
126 snaps = json.loads(flask.request.form.get("snaps"))
128 res = {}
130 dashboard.update_store_snaps(flask.session, store_id, snaps)
131 res["msg"] = "Changes saved"
133 return jsonify({"success": True})