Coverage for webapp/helpers.py: 84%
101 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-14 22:07 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-14 22:07 +0000
1import json
2import os
3import hashlib
4import sentry_sdk
6import flask
7from canonicalwebteam.launchpad import Launchpad
8from ruamel.yaml import YAML
9from webapp.api.requests import PublisherSession, Session
10from canonicalwebteam.store_api.dashboard import Dashboard
11import webapp.api.marketo as marketo_api
13_yaml = YAML(typ="rt")
14_yaml_safe = YAML(typ="safe")
15api_session = Session()
16api_publisher_session = PublisherSession()
17marketo = marketo_api.Marketo()
18dashboard = Dashboard(api_session)
20launchpad = Launchpad(
21 username=os.getenv("LP_API_USERNAME"),
22 token=os.getenv("LP_API_TOKEN"),
23 secret=os.getenv("LP_API_TOKEN_SECRET"),
24 session=api_publisher_session,
25)
28def get_yaml_loader(typ="safe"):
29 if typ == "safe":
30 return _yaml_safe
31 return _yaml
34def get_licenses():
35 try:
36 with open("webapp/licenses.json") as f:
37 licenses = json.load(f)["licenses"]
39 def _build_custom_license(license_id, license_name):
40 return {"licenseId": license_id, "name": license_name}
42 CUSTOM_LICENSES = [
43 _build_custom_license("Proprietary", "Proprietary"),
44 _build_custom_license("Other Open Source", "Other Open Source"),
45 _build_custom_license(
46 "AGPL-3.0+", "GNU Affero General Public License v3.0 or later"
47 ),
48 ]
50 licenses = licenses + CUSTOM_LICENSES
51 except Exception:
52 licenses = []
54 return licenses
57def is_valid_path(path):
58 base_path = os.path.abspath(flask.current_app.root_path)
59 target_path = os.path.abspath(os.path.join(base_path, path))
60 return target_path.startswith(base_path)
63def get_file(filename, replaces={}):
64 """
65 Reads a file, replaces occurences of all the keys in `replaces` with
66 the correspondant values and returns the resulting string or None
68 Keyword arguments:
69 filename -- name if the file to load.
70 replaces -- key/values to replace in the file content (default {})
71 """
72 if not is_valid_path(filename):
73 return None
75 filepath = os.path.join(flask.current_app.root_path, filename)
77 try:
78 with open(filepath, "r") as f:
79 data = f.read()
80 for key in replaces:
81 data = data.replace(key, replaces[key])
82 except Exception:
83 data = None
85 return data
88def get_yaml(filename, typ="safe", replaces={}):
89 """
90 Reads a file, replaces occurences of all the keys in `replaces` with the
91 correspondant values and returns an ordered dict with the YAML content
93 Keyword arguments:
94 filename -- name if the file to load.
95 typ -- type of yaml loader
96 replaces -- key/values to replace in the file content (default {})
97 """
98 try:
99 yaml = get_yaml_loader(typ)
100 data = get_file(filename, replaces)
101 return yaml.load(data)
102 except Exception:
103 return None
106def dump_yaml(data, stream, typ="safe"):
107 yaml = get_yaml_loader(typ)
108 yaml.dump(data, stream)
111def get_icon(media):
112 icons = [m["url"] for m in media if m["type"] == "icon"]
113 if len(icons) > 0:
114 return icons[0]
115 return ""
118def get_publisher_data():
119 # We don't use the data from this endpoint.
120 # It is mostly used to make sure the user has signed
121 # the terms and conditions.
122 dashboard.get_account(flask.session)
124 flask_user = flask.session["publisher"]
126 subscriptions = None
128 # don't rely on marketo to show the page,
129 # if anything fails, just continue and don't show
130 # this section
131 try:
132 subscribed_to_newsletter = False
133 marketo_user = marketo.get_user(flask_user["email"])
134 if marketo_user:
135 marketo_subscribed = marketo.get_newsletter_subscription(
136 marketo_user["id"]
137 )
138 if marketo_subscribed.get("snapcraftnewsletter"):
139 subscribed_to_newsletter = True
141 subscriptions = {"newsletter": subscribed_to_newsletter}
142 except Exception:
143 sentry_sdk.capture_exception()
145 flask_user["subscriptions"] = subscriptions
146 context = {"publisher": flask_user}
148 return context
151def get_dns_verification_token(snap_name, domain):
152 salt = os.getenv("DNS_VERIFICATION_SALT")
153 token_string = f"{domain}:{snap_name}:{salt}"
154 token = hashlib.sha256(token_string.encode("utf-8")).hexdigest()
155 return token
158def get_csp_as_str(csp={}):
159 csp_str = ""
160 for key, values in csp.items():
161 csp_value = " ".join(values)
162 csp_str += f"{key} {csp_value}; "
163 return csp_str.strip()
166def list_folders(directory):
167 return [
168 item
169 for item in os.listdir(directory)
170 if os.path.isdir(os.path.join(directory, item))
171 ]
174def directory_exists(file):
175 if not is_valid_path(file):
176 return False
178 target_path = os.path.abspath(
179 os.path.join(flask.current_app.root_path, file)
180 )
181 return os.path.isdir(target_path)
184def get_brand_id(session, store_id):
185 store = dashboard.get_store(session, store_id)
186 return store["brand-id"]