Coverage for webapp/app.py: 99%

70 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-20 22:09 +0000

1""" 

2A Flask application for snapcraft.io. 

3 

4The web frontend for the snap store. 

5""" 

6 

7# We import the config module before anything else to make sure env vars are 

8# loaded properly and the FLASK_* prefix is stripped before they are parsed 

9import webapp.config # noqa: F401 

10 

11import sentry_sdk 

12from flask import send_from_directory 

13 

14from canonicalwebteam.flask_base.app import FlaskBase 

15from webapp.blog.views import init_blog 

16from webapp.docs.views import init_docs 

17from webapp.extensions import csrf, vite 

18from webapp.handlers import set_handlers 

19from webapp.login.views import login 

20from webapp.login.oauth_views import oauth 

21from webapp.publisher.snaps.views import publisher_snaps 

22from webapp.publisher.github.views import publisher_github 

23from webapp.admin.views import admin 

24from webapp.publisher.views import account 

25from webapp.snapcraft.views import snapcraft_blueprint 

26from webapp.store.views import store_blueprint 

27from webapp.tutorials.views import init_tutorials 

28from webapp.packages.store_packages import store_packages 

29from webapp.endpoints.views import endpoints 

30from webapp.endpoints.signing_keys import signing_keys 

31from webapp.endpoints.models import models 

32from webapp.endpoints.members import members 

33from webapp.endpoints.snaps import snaps 

34from webapp.endpoints.snap_search import snap_search 

35from webapp.endpoints.validation_sets import validation_sets 

36from webapp.endpoints.invites import invites 

37from webapp.endpoints.settings import settings 

38from webapp.feeds.feeds import feeds 

39from webapp.config import SENTRY_DSN 

40 

41 

42def create_app(testing=False): 

43 sentry_sdk.init(dsn=SENTRY_DSN) 

44 

45 app = FlaskBase( 

46 __name__, 

47 "snapcraft.io", 

48 favicon_url="https://assets.ubuntu.com/v1/d4ca039f-favicon_16px.png", 

49 template_404="404.html", 

50 template_folder="../templates", 

51 static_folder="../static", 

52 ) 

53 app.config.from_object("webapp.config") 

54 app.name = "snapcraft" 

55 app.testing = testing 

56 

57 # This is required because Pragma icons are served from `/icons` at the 

58 # root of the project, which is a problem for snapcraft.io as that is 

59 # the URL pattern we use for snap names. To resolve this, we are 

60 # serving any requests to `/icons/<ICON_NAME>.svg` from static icons, 

61 # which leaves the possibility for a snap called "icons" and its 

62 # subsequent publisher pages. 

63 # 

64 # DS icons are exposed through the tracked `static/icons` symlink. 

65 def serve_ds_icon(icon_name): 

66 return send_from_directory( 

67 f"{app.static_folder}/icons", 

68 f"{icon_name}.svg", 

69 ) 

70 

71 app.add_url_rule( 

72 "/icons/<icon_name>.svg", 

73 "ds-icons", 

74 serve_ds_icon, 

75 ) 

76 

77 init_extensions(app) 

78 set_handlers(app) 

79 

80 app.register_blueprint(snapcraft_blueprint()) 

81 app.register_blueprint(store_packages) 

82 app.register_blueprint(login) 

83 app.register_blueprint(oauth) 

84 app.register_blueprint(store_blueprint()) 

85 app.register_blueprint(account, url_prefix="/account") 

86 app.register_blueprint(publisher_snaps) 

87 app.register_blueprint(publisher_github) 

88 app.register_blueprint(admin) 

89 app.register_blueprint(endpoints) 

90 app.register_blueprint(signing_keys) 

91 app.register_blueprint(models) 

92 app.register_blueprint(members) 

93 app.register_blueprint(snaps) 

94 app.register_blueprint(snap_search) 

95 app.register_blueprint(validation_sets) 

96 app.register_blueprint(invites) 

97 app.register_blueprint(settings) 

98 app.register_blueprint(feeds) 

99 init_docs(app, "/docs") 

100 init_blog(app, "/blog") 

101 init_tutorials(app, "/tutorials") 

102 

103 return app 

104 

105 

106def init_extensions(app: FlaskBase): 

107 vite.init_app(app) 

108 

109 if not app.testing: 

110 csrf.init_app(app) 

111 else: 

112 # add a helper for injecting a mock CSRF token 

113 @app.context_processor 

114 def inject_csrf_token(): 

115 return dict(csrf_token=lambda: "mocked_csrf_token")