def test_parse_single_fences(self): """Code (text blocks inside ` pairs)""" markdown = "`code block`" result = parse_markdown_description(markdown) expected_result = "<p><code>code block</code></p>\n" self.assertEqual(result, expected_result)
def test_parse_text(self): """Text conversion works""" markdown = "text" result = parse_markdown_description(markdown) expected_result = "<p>text</p>\n" self.assertEqual(result, expected_result)
def test_parse_triple_fences(self): """Code (text blocks inside ``` pairs)""" markdown = "```code block```" result = parse_markdown_description(markdown) expected_result = "<p>```code block```</p>\n" self.assertEqual(result, expected_result)
def test_parse_paragraph_merging(self): """Paragraph merging (consecutive lines are joined)""" markdown = "this is\n a paragraph" result = parse_markdown_description(markdown) expected_result = "<p>this is\n a paragraph</p>\n" self.assertEqual(result, expected_result)
def test_parse_paragraph(self): """Paragraphs""" markdown = "paragraph 1\n\n paragraph 2" result = parse_markdown_description(markdown) expected_result = "<p>paragraph 1</p>\n<p>paragraph 2</p>\n" self.assertEqual(result, expected_result)
def test_parse_italics(self): """Italics (_foo_)""" markdown = "_text_" result = parse_markdown_description(markdown) expected_result = "<p><em>text</em></p>\n" self.assertEqual(result, expected_result)
def test_parse_code_line(self): """Code (text blocks inside `)""" markdown = "`code line`" result = parse_markdown_description(markdown) expected_result = "<p><code>code line</code></p>\n" self.assertEqual(result, expected_result)
def test_parse_image_link(self): """Image link is converted into a simple link""" markdown = "![image](link.png)" result = parse_markdown_description(markdown) expected_result = "<p>" + markdown + "</p>\n" self.assertEqual(result, expected_result)
def test_parse_title(self): """Title conversion shouldn't work""" markdown = "# title" result = parse_markdown_description(markdown) expected_result = "<p># title</p>\n" self.assertEqual(result, expected_result)
def test_parse_code_block_single_line(self): """Code with three space indentation""" markdown = " code" result = parse_markdown_description(markdown) expected_result = "<pre><code>code\n</code></pre>\n" self.assertEqual(result, expected_result)
def test_parse_bold(self): """Bold (**foo**)""" markdown = "**text**" result = parse_markdown_description(markdown) expected_result = "<p><strong>text</strong></p>\n" self.assertEqual(result, expected_result)
def test_parse_urls(self): """Literal URLs auto-link https://foo.bar""" markdown = "https://toto.space" result = parse_markdown_description(markdown) expected_result = ( '<p><a href="https://toto.space">https://toto.space</a></p>\n') self.assertEqual(result, expected_result)
def test_parse_list_ordered(self): """Lists (* Foo)""" markdown = "1. item \n2. item \n3. item \n" result = parse_markdown_description(markdown) expected_result = ( "<ol>\n<li>item </li>\n<li>item </li>\n<li>item </li>\n</ol>\n") self.assertEqual(result, expected_result)
def test_parse_list_special_char(self): """Lists (• Foo)""" markdown = "• item \n• item \n• item \n" result = parse_markdown_description(markdown) expected_result = ( "<ul>\n<li>item </li>\n<li>item </li>\n<li>item </li>\n</ul>\n") self.assertEqual(result, expected_result)
def test_parse_urls_title(self): """URLs with title [title for the link](https://foo.bar)""" markdown = "[toto](https://toto.space)" result = parse_markdown_description(markdown) expected_result = ( "<p>" '[toto](<a href="https://toto.space">https://toto.space</a>)' "</p>\n") self.assertEqual(result, expected_result)
def post_preview(snap_name): try: snap_details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) context = { "publisher": snap_details["publisher"]["display-name"], "username": snap_details["publisher"]["username"], "developer_validation": snap_details["publisher"]["validation"], } state = loads(flask.request.form["state"]) for item in state: if item == "description": context[item] = parse_markdown_description( bleach.clean(state[item], tags=[])) else: context[item] = state[item] context["is_preview"] = True context["package_name"] = context["snap_name"] context["snap_title"] = context["title"] context["appliances"] = [] # Images icons = get_icon(context["images"]) context["screenshots"] = filter_screenshots(context["images"]) context["icon_url"] = icons[0] if icons else None context["video"] = get_video(context["images"]) # Channel map context["channel_map"] = [] context["default_track"] = "latest" context["lowest_risk_available"] = "stable" context["version"] = "test" context["has_stable"] = True # metadata context["last_updated"] = "Preview" context["filesize"] = "1mb" # maps context["countries"] = preview_data.get_countries() context["normalized_os"] = preview_data.get_normalised_oses() return flask.render_template("store/snap-details.html", **context)
def post_preview(snap_name): try: snap_details = api.get_snap_info(snap_name, flask.session) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_errors(api_error) context = { "publisher": snap_details["publisher"]["display-name"], "username": snap_details["publisher"]["username"], "developer_validation": snap_details["publisher"]["validation"], } state = loads(flask.request.form["state"]) for item in state: if item == "description": context[item] = parse_markdown_description( bleach.clean(state[item]) ) else: context[item] = state[item] context["is_preview"] = True context["package_name"] = context["snap_name"] context["snap_title"] = context["title"] # Images icons = get_icon(context["images"]) context["screenshots"] = filter_screenshots(context["images"]) context["icon_url"] = icons[0] if icons else None context["videos"] = get_videos(context["images"]) # Channel map context["default_track"] = "latest" context["lowest_risk_available"] = "stable" context["version"] = "test" context["has_stable"] = True # metadata context["last_updated"] = "Preview" context["filesize"] = "1mb" # maps context["countries"] = preview_data.get_countries() context["normalized_os"] = preview_data.get_normalised_oses() return flask.render_template("store/snap-details.html", **context)
def _get_context_snap_details(snap_name): try: details = api.get_snap_details(snap_name) except ApiTimeoutError as api_timeout_error: flask.abort(504, str(api_timeout_error)) except ApiResponseDecodeError as api_response_decode_error: flask.abort(502, str(api_response_decode_error)) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: flask.abort(404, "No snap named {}".format(snap_name)) else: if api_response_error_list.errors: error_messages = ", ".join( api_response_error_list.errors.key() ) else: error_messages = "An error occurred." flask.abort(502, error_messages) except ApiResponseError as api_response_error: flask.abort(502, str(api_response_error)) except ApiCircuitBreaker: flask.abort(503) except ApiError as api_error: flask.abort(502, str(api_error)) # When removing all the channel maps of an exsting snap the API, # responds that the snaps still exists with data. # Return a 404 if not channel maps, to avoid having a error. # For example: mir-kiosk-browser if not details.get("channel-map"): flask.abort(404, "No snap named {}".format(snap_name)) clean_description = bleach.clean(details["snap"]["description"]) formatted_description = parse_markdown_description(clean_description) channel_maps_list = logic.convert_channel_maps( details.get("channel-map") ) latest_channel = logic.get_last_updated_version( details.get("channel-map") ) last_updated = latest_channel["created-at"] last_version = latest_channel["version"] binary_filesize = latest_channel["download"]["size"] # filter out banner and banner-icon images from screenshots screenshots = logic.filter_screenshots(details["snap"]["media"]) icons = logic.get_icon(details["snap"]["media"]) videos = logic.get_videos(details["snap"]["media"]) # until default tracks are supported by the API we special case node # to use 10, rather then latest default_track = helpers.get_default_track(details["name"]) lowest_risk_available = logic.get_lowest_available_risk( channel_maps_list, default_track ) confinement = logic.get_confinement( channel_maps_list, default_track, lowest_risk_available ) last_version = logic.get_version( channel_maps_list, default_track, lowest_risk_available ) is_users_snap = False if flask.session and "openid" in flask.session: if ( flask.session.get("openid").get("nickname") == details["snap"]["publisher"]["username"] ): is_users_snap = True # build list of categories of a snap categories = logic.get_snap_categories(details["snap"]["categories"]) context = { "snap-id": details.get("snap-id"), # Data direct from details API "snap_title": details["snap"]["title"], "package_name": details["name"], "categories": categories, "icon_url": icons[0] if icons else None, "version": last_version, "license": details["snap"]["license"], "publisher": details["snap"]["publisher"]["display-name"], "username": details["snap"]["publisher"]["username"], "screenshots": screenshots, "videos": videos, "prices": details["snap"]["prices"], "contact": details["snap"].get("contact"), "website": details["snap"].get("website"), "summary": details["snap"]["summary"], "description": formatted_description, "channel_map": channel_maps_list, "has_stable": logic.has_stable(channel_maps_list), "developer_validation": details["snap"]["publisher"]["validation"], "default_track": default_track, "lowest_risk_available": lowest_risk_available, "confinement": confinement, # Transformed API data "filesize": humanize.naturalsize(binary_filesize), "last_updated": logic.convert_date(last_updated), "last_updated_raw": last_updated, "is_users_snap": is_users_snap, } return context
def _get_context_snap_details(snap_name): try: details = api.get_item_details(snap_name, api_version=2) except StoreApiTimeoutError as api_timeout_error: flask.abort(504, str(api_timeout_error)) except StoreApiResponseDecodeError as api_response_decode_error: flask.abort(502, str(api_response_decode_error)) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: flask.abort(404, "No snap named {}".format(snap_name)) else: if api_response_error_list.errors: error_messages = ", ".join( api_response_error_list.errors.key()) else: error_messages = "An error occurred." flask.abort(502, error_messages) except StoreApiResponseError as api_response_error: flask.abort(502, str(api_response_error)) except StoreApiCircuitBreaker: flask.abort(503) except (StoreApiError, ApiError) as api_error: flask.abort(502, str(api_error)) # When removing all the channel maps of an existing snap the API, # responds that the snaps still exists with data. # Return a 404 if not channel maps, to avoid having a error. # For example: mir-kiosk-browser if not details.get("channel-map"): flask.abort(404, "No snap named {}".format(snap_name)) clean_description = bleach.clean(details["snap"]["description"], tags=[]) formatted_description = parse_markdown_description(clean_description) channel_maps_list = logic.convert_channel_maps( details.get("channel-map")) latest_channel = logic.get_last_updated_version( details.get("channel-map")) last_updated = latest_channel["created-at"] last_version = latest_channel["version"] binary_filesize = latest_channel["download"]["size"] # filter out banner and banner-icon images from screenshots screenshots = logic.filter_screenshots(details["snap"]["media"]) icons = logic.get_icon(details["snap"]["media"]) publisher_info = helpers.get_yaml( "{}{}.yaml".format( flask.current_app.config["CONTENT_DIRECTORY"] ["PUBLISHER_PAGES"], details["snap"]["publisher"]["username"], ), typ="safe", ) publisher_snaps = helpers.get_yaml( "{}{}-snaps.yaml".format( flask.current_app.config["CONTENT_DIRECTORY"] ["PUBLISHER_PAGES"], details["snap"]["publisher"]["username"], ), typ="safe", ) publisher_featured_snaps = None if publisher_info: publisher_featured_snaps = publisher_info.get("featured_snaps") publisher_snaps = logic.get_n_random_snaps( publisher_snaps["snaps"], 4) videos = logic.get_videos(details["snap"]["media"]) # until default tracks are supported by the API we special case node # to use 10, rather then latest default_track = helpers.get_default_track(details["name"]) if not default_track: default_track = (details.get("default-track") if details.get("default-track") else "latest") lowest_risk_available = logic.get_lowest_available_risk( channel_maps_list, default_track) confinement = logic.get_confinement(channel_maps_list, default_track, lowest_risk_available) last_version = logic.get_version(channel_maps_list, default_track, lowest_risk_available) is_users_snap = False if authentication.is_authenticated(flask.session): if (flask.session.get("openid").get("nickname") == details["snap"]["publisher"]["username"] ) or ("user_shared_snaps" in flask.session and snap_name in flask.session.get("user_shared_snaps")): is_users_snap = True # build list of categories of a snap categories = logic.get_snap_categories(details["snap"]["categories"]) developer = logic.get_snap_developer(details["name"]) context = { "snap-id": details.get("snap-id"), # Data direct from details API "snap_title": details["snap"]["title"], "package_name": details["name"], "categories": categories, "icon_url": icons[0] if icons else None, "version": last_version, "license": details["snap"]["license"], "publisher": details["snap"]["publisher"]["display-name"], "username": details["snap"]["publisher"]["username"], "screenshots": screenshots, "videos": videos, "publisher_snaps": publisher_snaps, "publisher_featured_snaps": publisher_featured_snaps, "has_publisher_page": publisher_info is not None, "prices": details["snap"]["prices"], "contact": details["snap"].get("contact"), "website": details["snap"].get("website"), "summary": details["snap"]["summary"], "description": formatted_description, "channel_map": channel_maps_list, "has_stable": logic.has_stable(channel_maps_list), "developer_validation": details["snap"]["publisher"]["validation"], "default_track": default_track, "lowest_risk_available": lowest_risk_available, "confinement": confinement, "trending": details["snap"]["trending"], # Transformed API data "filesize": humanize.naturalsize(binary_filesize), "last_updated": logic.convert_date(last_updated), "last_updated_raw": last_updated, "is_users_snap": is_users_snap, "unlisted": details["snap"]["unlisted"], "developer": developer, # TODO: This is horrible and hacky "appliances": { "adguard-home": "adguard", "mosquitto": "mosquitto", "nextcloud": "nextcloud", "plexmediaserver": "plex", "openhab": "openhab", }, } return context
def _get_context_snap_details(snap_name): try: details = api.get_snap_details(snap_name) except ApiTimeoutError as api_timeout_error: flask.abort(504, str(api_timeout_error)) except ApiResponseDecodeError as api_response_decode_error: flask.abort(502, str(api_response_decode_error)) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: flask.abort(404, "No snap named {}".format(snap_name)) else: if api_response_error_list.errors: error_messages = ", ".join( api_response_error_list.errors.key()) else: error_messages = "An error occurred." flask.abort(502, error_messages) except ApiResponseError as api_response_error: flask.abort(502, str(api_response_error)) except ApiCircuitBreaker: flask.abort(503) except ApiError as api_error: flask.abort(502, str(api_error)) # When removing all the channel maps of an exsting snap the API, # responds that the snaps still exists with data. # Return a 404 if not channel maps, to avoid having a error. # For example: mir-kiosk-browser if not details.get("channel-map"): flask.abort(404, "No snap named {}".format(snap_name)) clean_description = bleach.clean(details["snap"]["description"], tags=[]) formatted_description = parse_markdown_description(clean_description) channel_maps_list = logic.convert_channel_maps( details.get("channel-map")) latest_channel = logic.get_last_updated_version( details.get("channel-map")) last_updated = latest_channel["created-at"] last_version = latest_channel["version"] binary_filesize = latest_channel["download"]["size"] # filter out banner and banner-icon images from screenshots screenshots = logic.filter_screenshots(details["snap"]["media"]) icons = logic.get_icon(details["snap"]["media"]) videos = logic.get_videos(details["snap"]["media"]) # until default tracks are supported by the API we special case node # to use 10, rather then latest default_track = helpers.get_default_track(details["name"]) lowest_risk_available = logic.get_lowest_available_risk( channel_maps_list, default_track) confinement = logic.get_confinement(channel_maps_list, default_track, lowest_risk_available) last_version = logic.get_version(channel_maps_list, default_track, lowest_risk_available) is_users_snap = False if flask.session and "openid" in flask.session: if (flask.session.get("openid").get("nickname") == details["snap"] ["publisher"]["username"]): is_users_snap = True # build list of categories of a snap categories = logic.get_snap_categories(details["snap"]["categories"]) context = { "snap-id": details.get("snap-id"), # Data direct from details API "snap_title": details["snap"]["title"], "package_name": details["name"], "categories": categories, "icon_url": icons[0] if icons else None, "version": last_version, "license": details["snap"]["license"], "publisher": details["snap"]["publisher"]["display-name"], "username": details["snap"]["publisher"]["username"], "screenshots": screenshots, "videos": videos, "prices": details["snap"]["prices"], "contact": details["snap"].get("contact"), "website": details["snap"].get("website"), "summary": details["snap"]["summary"], "description": formatted_description, "channel_map": channel_maps_list, "has_stable": logic.has_stable(channel_maps_list), "developer_validation": details["snap"]["publisher"]["validation"], "default_track": default_track, "lowest_risk_available": lowest_risk_available, "confinement": confinement, # Transformed API data "filesize": humanize.naturalsize(binary_filesize), "last_updated": logic.convert_date(last_updated), "last_updated_raw": last_updated, "is_users_snap": is_users_snap, } return context