Coverage for webapp/endpoints/publisher/listing.py: 83%

110 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2025-08-26 22:06 +0000

1# Standard library 

2from json import loads 

3 

4# Packages 

5import flask 

6from canonicalwebteam.exceptions import ( 

7 StoreApiError, 

8 StoreApiResponseErrorList, 

9) 

10from canonicalwebteam.store_api.dashboard import Dashboard 

11from canonicalwebteam.store_api.devicegw import DeviceGW 

12 

13# Local 

14from webapp import helpers 

15from webapp.helpers import api_session 

16from webapp.decorators import login_required 

17from webapp.publisher.snaps import logic 

18from webapp.store.logic import ( 

19 get_categories, 

20) 

21 

22dashboard = Dashboard(api_session) 

23device_gateway = DeviceGW("snap", api_session) 

24 

25 

26@login_required 

27def get_listing_data(snap_name): 

28 snap_details = dashboard.get_snap_info(flask.session, snap_name) 

29 

30 details_metrics_enabled = snap_details["public_metrics_enabled"] 

31 details_blacklist = snap_details.get("public_metrics_blacklist", []) 

32 

33 # Filter icon & screenshot urls from the media set. 

34 icon_urls, screenshot_urls, banner_urls = logic.categorise_media( 

35 snap_details["media"] 

36 ) 

37 

38 primary_website = "" 

39 if ( 

40 "website" in snap_details["links"] 

41 and len(snap_details["links"]["website"]) > 0 

42 ): 

43 primary_website = snap_details["links"]["website"][0] 

44 

45 websites = [] 

46 if "website" in snap_details["links"]: 

47 if len(snap_details["links"]["website"]) > 1: 

48 snap_details["links"]["website"].pop(0) 

49 for website in snap_details["links"]["website"]: 

50 websites.append({"url": website}) 

51 

52 def format_links(key): 

53 result = [] 

54 if key in snap_details["links"]: 

55 for url in snap_details["links"][key]: 

56 result.append({"url": url}) 

57 return result 

58 

59 licenses = [] 

60 for license in helpers.get_licenses(): 

61 licenses.append({"key": license["licenseId"], "name": license["name"]}) 

62 

63 license = snap_details["license"] 

64 license_type = "custom" 

65 

66 if " AND " not in license.upper() and " WITH " not in license.upper(): 

67 license_type = "simple" 

68 

69 try: 

70 categories_results = device_gateway.get_categories() 

71 except StoreApiError: 

72 categories_results = [] 

73 

74 categories = sorted( 

75 get_categories(categories_results), 

76 key=lambda category: category["slug"], 

77 ) 

78 

79 snap_categories = logic.replace_reserved_categories_key( 

80 snap_details["categories"] 

81 ) 

82 

83 snap_categories = logic.filter_categories(snap_categories) 

84 

85 snap_categories["categories"] = [ 

86 category["name"] for category in snap_categories["categories"] 

87 ] 

88 

89 filename = "publisher/content/listing_tour.yaml" 

90 tour_steps = helpers.get_yaml(filename, typ="rt") 

91 

92 primary_category = "" 

93 if len(snap_categories["categories"]) > 0: 

94 primary_category = snap_categories["categories"][0] 

95 

96 secondary_category = "" 

97 if len(snap_categories["categories"]) > 1: 

98 secondary_category = snap_categories["categories"][1] 

99 

100 video_urls = None 

101 

102 if len(snap_details["video_urls"]) > 0: 

103 video_urls = snap_details["video_urls"][0] 

104 

105 context = { 

106 "title": snap_details["title"], 

107 "video_urls": video_urls, 

108 "summary": snap_details["summary"], 

109 "description": snap_details["description"], 

110 "categories": categories, 

111 "primary_category": primary_category, 

112 "secondary_category": secondary_category, 

113 "websites": websites, 

114 "contacts": format_links("contact"), 

115 "donations": format_links("donations"), 

116 "source_code": format_links("source"), 

117 "issues": format_links("issues"), 

118 "primary_website": primary_website, 

119 "snap_id": snap_details["snap_id"], 

120 "public_metrics_enabled": details_metrics_enabled, 

121 "public_metrics_blacklist": details_blacklist, 

122 "license": license, 

123 "license_type": license_type, 

124 "licenses": licenses, 

125 "icon_url": icon_urls[0] if icon_urls else None, 

126 "screenshot_urls": screenshot_urls, 

127 "banner_urls": banner_urls, 

128 "update_metadata_on_release": snap_details[ 

129 "update_metadata_on_release" 

130 ], 

131 "tour_steps": tour_steps, 

132 } 

133 

134 res = {} 

135 res["data"] = context 

136 res["message"] = "" 

137 res["success"] = True 

138 

139 return flask.jsonify(res) 

140 

141 

142@login_required 

143def post_listing_data(snap_name): 

144 changes = None 

145 changed_fields = flask.request.form.get("changes") 

146 

147 if changed_fields: 

148 changes = loads(changed_fields) 

149 

150 if changes: 

151 snap_id = flask.request.form.get("snap_id") 

152 error_list = [] 

153 

154 if "images" in changes: 

155 # Add existing screenshots 

156 current_screenshots = dashboard.snap_screenshots( 

157 flask.session, snap_id 

158 ) 

159 

160 icon_input = ( 

161 flask.request.files.get("icon") 

162 if flask.request.files.get("icon") 

163 else None 

164 ) 

165 screenshots_input = [ 

166 s if s else None 

167 for s in flask.request.files.getlist("screenshots") 

168 ] 

169 banner_image_input = ( 

170 flask.request.files.get("banner-image") 

171 if flask.request.files.get("banner-image") 

172 else None 

173 ) 

174 

175 images_json, images_files = logic.build_changed_images( 

176 changes["images"], 

177 current_screenshots, 

178 icon_input, 

179 screenshots_input, 

180 banner_image_input, 

181 ) 

182 

183 try: 

184 dashboard.snap_screenshots( 

185 flask.session, snap_id, images_json, images_files 

186 ) 

187 except StoreApiResponseErrorList as api_response_error_list: 

188 if api_response_error_list.status_code != 404: 

189 error_list = error_list + api_response_error_list.errors 

190 

191 body_json = logic.filter_changes_data(changes) 

192 

193 if body_json: 

194 if "description" in body_json: 

195 body_json["description"] = logic.remove_invalid_characters( 

196 body_json["description"] 

197 ) 

198 

199 try: 

200 dashboard.snap_metadata(flask.session, snap_id, body_json) 

201 except StoreApiResponseErrorList as api_response_error_list: 

202 if api_response_error_list.status_code != 404: 

203 error_list = error_list + api_response_error_list.errors 

204 

205 if error_list: 

206 try: 

207 snap_details = dashboard.get_snap_info( 

208 flask.session, snap_name 

209 ) 

210 except StoreApiResponseErrorList as api_response_error_list: 

211 if api_response_error_list.status_code == 404: 

212 return flask.abort( 

213 404, "No snap named {}".format(snap_name) 

214 ) 

215 else: 

216 error_list = error_list + api_response_error_list.errors 

217 

218 licenses = [] 

219 for license in helpers.get_licenses(): 

220 licenses.append( 

221 {"key": license["licenseId"], "name": license["name"]} 

222 ) 

223 

224 license = snap_details["license"] 

225 

226 snap_categories = logic.replace_reserved_categories_key( 

227 snap_details["categories"] 

228 ) 

229 

230 snap_categories = logic.filter_categories(snap_categories) 

231 

232 res = {"success": True, "errors": error_list} 

233 

234 return flask.make_response(res, 200) 

235 

236 return flask.make_response({"success": True}, 200)