Example #1
0
 def before_init_app(self, app: FlaskUnchained):
     app.jinja_env.add_extension('jinja2.ext.i18n')
     babel.locale_selector_func = self.get_locale
     if app.config.get('ENABLE_URL_LANG_CODE_PREFIX',
                       BabelBundleConfig.ENABLE_URL_LANG_CODE_PREFIX):
         app.url_value_preprocessor(self.lang_code_url_value_preprocessor)
         app.url_defaults(self.set_url_defaults)
Example #2
0
 def after_init_app(self, app: FlaskUnchained):
     """
     Configure the JSON encoder for Flask to be able to serialize Enums,
     LocalProxy objects, and SQLAlchemy models.
     """
     self.set_json_encoder(app)
     app.before_first_request(self.register_model_resources)
Example #3
0
 def register_blueprint(self, app: FlaskUnchained, blueprint: Blueprint, **options):
     if app.config.get('ENABLE_URL_LANG_CODE_PREFIX'):
         url_prefix = (options.get('url_prefix', (blueprint.url_prefix or ''))
                              .rstrip('/'))
         options = dict(**options,
                        url_prefix=self.get_url_rule(url_prefix),
                        register_with_babel=False)
         app.register_blueprint(blueprint, **options)
Example #4
0
 def process_objects(self, app: FlaskUnchained,
                     blueprints: List[Blueprint]):
     for blueprint in reversed(blueprints):
         # rstrip '/' off url_prefix because views should be declaring their
         # routes beginning with '/', and if url_prefix ends with '/', routes
         # will end up looking like '/prefix//endpoint', which is no good
         url_prefix = (blueprint.url_prefix or '').rstrip('/')
         app.register_blueprint(blueprint, url_prefix=url_prefix)
    def process_objects(self, app: FlaskUnchained, routes: Iterable[Route]):
        for route in _reduce_routes(routes):
            if route.should_register(app):
                if route.module_name and route in self.bundle.endpoints[
                        route.endpoint]:
                    import warnings
                    warnings.warn(f'Duplicate route found: {route}')
                    continue

                self.bundle.endpoints[route.endpoint].append(route)
                if route._controller_cls:
                    key = f'{route._controller_cls.__name__}.{route.method_name}'
                    self.bundle.controller_endpoints[key].append(route)

        # build up a list of bundles with views:
        bundle_module_names = [
        ]  # [tuple(top_bundle_module_name, hierarchy_module_names)]
        for bundle in app.unchained.bundles.values():
            hierarchy = [
                bundle_super.module_name
                for bundle_super in bundle._iter_class_hierarchy()
                if bundle_super._has_views
            ]
            if hierarchy:
                bundle_module_names.append((bundle.module_name, hierarchy))

        # for each route, figure out which bundle hierarchy it's from, and assign the
        # route to the top bundle for that hierarchy
        bundle_route_endpoints = set()
        for endpoint, routes in self.bundle.endpoints.items():
            for route in routes:
                if not route.module_name:
                    continue

                # FIXME would be nice for routes to know which bundle they're from...
                for top_level_bundle_module_name, hierarchy in bundle_module_names:
                    for bundle_module_name in hierarchy:
                        if route.module_name.startswith(bundle_module_name):
                            self.bundle.bundle_routes[
                                top_level_bundle_module_name].append(route)
                            bundle_route_endpoints.add(endpoint)
                            break

        # get all the remaining routes not belonging to a bundle
        self.bundle.other_routes = itertools.chain.from_iterable([
            routes for endpoint, routes in self.bundle.endpoints.items()
            if endpoint not in bundle_route_endpoints
        ])

        # we register non-bundle routes with the app here, and
        # the RegisterBundleBlueprintsHook registers the bundle routes
        for route in self.bundle.other_routes:
            app.add_url_rule(route.full_rule,
                             defaults=route.defaults,
                             endpoint=route.endpoint,
                             methods=route.methods,
                             view_func=route.view_func,
                             **route.rule_options)
Example #6
0
 def before_init_app(self, app: FlaskUnchained):
     """
     Configure the Jinja environment and template loader.
     """
     from .templates import (UnchainedJinjaEnvironment,
                             UnchainedJinjaLoader)
     app.jinja_environment = UnchainedJinjaEnvironment
     app.jinja_options = {
         **app.jinja_options, 'loader': UnchainedJinjaLoader(app)
     }
     app.jinja_env.globals['url_for'] = url_for
Example #7
0
    def before_init_app(self, app: FlaskUnchained):
        """
        Configure the Jinja environment and template loader.
        """
        from .templates import (UnchainedJinjaEnvironment,
                                UnchainedJinjaLoader)
        app.jinja_environment = UnchainedJinjaEnvironment
        app.jinja_options = {
            **app.jinja_options, 'loader': UnchainedJinjaLoader(app)
        }
        app.jinja_env.globals['url_for'] = url_for

        for name in ['string', 'str']:
            app.url_map.converters[name] = StringConverter
 def run_hook(self, app: FlaskUnchained, bundles: List[Bundle]):
     for bundle_ in reversed(bundles):
         for bundle in bundle_._iter_class_hierarchy(reverse=False):
             if (bundle.template_folder or bundle._static_folders
                     or bundle._has_views()):
                 bp = BundleBlueprint(bundle)
                 for route in self.bundle.bundle_routes.get(
                         bundle.module_name, []):
                     bp.add_url_rule(route.full_rule,
                                     defaults=route.defaults,
                                     endpoint=route.endpoint,
                                     methods=route.methods,
                                     view_func=route.view_func,
                                     **route.rule_options)
                 app.register_blueprint(bp)
Example #9
0
    def set_json_encoder(self, app: FlaskUnchained):
        from flask_unchained.bundles.sqlalchemy import BaseModel
        from flask_unchained import unchained
        from werkzeug.local import LocalProxy

        class JSONEncoder(app.json_encoder):
            def default(self, obj):
                if isinstance(obj, LocalProxy):
                    obj = obj._get_current_object()

                if isinstance(obj, enum.Enum):
                    return obj.name
                elif isinstance(obj, _LazyString):
                    return str(obj)

                api_bundle = unchained.api_bundle
                if isinstance(obj, BaseModel):
                    model_name = obj.__class__.__name__
                    serializer_cls = api_bundle.serializers_by_model.get(
                        model_name)
                    if serializer_cls:
                        return serializer_cls().dump(obj)

                elif (obj and isinstance(obj, (list, tuple))
                      and isinstance(obj[0], BaseModel)):
                    model_name = obj[0].__class__.__name__
                    serializer_cls = api_bundle.many_by_model.get(
                        model_name,
                        api_bundle.serializers_by_model.get(model_name))
                    if serializer_cls:
                        return serializer_cls(many=True).dump(obj)

                return super().default(obj)

        app.json_encoder = JSONEncoder
    def process_objects(self, app: FlaskUnchained, routes: Iterable[Route]):
        for route in _reduce_routes(routes):
            # FIXME maybe validate routes first? (eg for duplicates?)
            # Flask doesn't complain; it will match the first route found,
            # but maybe we should at least warn the user?
            if route.should_register(app):
                self.bundle.endpoints[route.endpoint].append(route)
                if route._controller_cls:
                    key = f'{route._controller_cls.__name__}.{route.method_name}'
                    self.bundle.controller_endpoints[key].append(route)

        bundle_names = [(
            bundle.module_name,
            [
                bundle_super.module_name
                for bundle_super in bundle._iter_class_hierarchy(
                    include_self=False) if bundle_super._has_views()
            ],
        ) for bundle in app.unchained.bundles.values()]

        bundle_route_endpoints = set()
        for endpoint, routes in self.bundle.endpoints.items():
            for route in routes:
                module_name = route.module_name
                for top_level_bundle_name, hierarchy in bundle_names:
                    for bundle_name in hierarchy:
                        if module_name and module_name.startswith(bundle_name):
                            self.bundle.bundle_routes[
                                top_level_bundle_name].append(route)
                            bundle_route_endpoints.add(endpoint)
                            break

        self.bundle.other_routes = itertools.chain.from_iterable([
            routes for endpoint, routes in self.bundle.endpoints.items()
            if endpoint not in bundle_route_endpoints
        ])

        for route in self.bundle.other_routes:
            app.add_url_rule(route.full_rule,
                             defaults=route.defaults,
                             endpoint=route.endpoint,
                             methods=route.methods,
                             view_func=route.view_func,
                             **route.rule_options)
Example #11
0
    def register_routes(self, app: FlaskUnchained):
        bp = Blueprint('api-docs',
                       __name__,
                       url_prefix=app.config.API_REDOC_URL_PREFIX.rstrip('/'),
                       template_folder=os.path.join(
                           os.path.dirname(os.path.abspath(__file__)),
                           'templates'))

        # Serve json spec at `API_REDOC_URL_PREFIX/openapi.json` by default
        bp.add_url_rule(app.config.API_OPENAPI_JSON_PATH,
                        endpoint='openapi_json',
                        view_func=self._openapi_json)

        # Serve ReDoc at `API_REDOC_URL_PREFIX/` by default
        bp.add_url_rule(app.config.API_REDOC_PATH,
                        endpoint='openapi_redoc',
                        view_func=self._openapi_redoc)

        app.register_blueprint(bp, register_with_babel=False)
Example #12
0
    def after_init_app(self, app: FlaskUnchained):
        if app.config.GRAPHENE_URL:
            app.add_url_rule(app.config.GRAPHENE_URL,
                             view_func=GraphQLView.as_view(
                                 'graphql',
                                 schema=self.root_schema,
                                 graphiql=app.config.GRAPHENE_ENABLE_GRAPHIQL,
                                 pretty=app.config.GRAPHENE_PRETTY_JSON,
                                 batch=False,
                             ))

        if app.config.GRAPHENE_BATCH_URL:
            app.add_url_rule(app.config.GRAPHENE_BATCH_URL,
                             view_func=GraphQLView.as_view(
                                 'graphql',
                                 schema=self.root_schema,
                                 graphiql=app.config.GRAPHENE_ENABLE_GRAPHIQL,
                                 pretty=app.config.GRAPHENE_PRETTY_JSON,
                                 batch=True,
                             ))
Example #13
0
    def init_app(self, app: FlaskUnchained):
        self.app = app
        self.name = app.config.ADMIN_NAME
        self.subdomain = app.config.ADMIN_SUBDOMAIN
        self._set_admin_index_view(app.config.ADMIN_INDEX_VIEW,
                                   url=app.config.ADMIN_BASE_URL)
        self.base_template = app.config.ADMIN_BASE_TEMPLATE
        self.template_mode = app.config.ADMIN_TEMPLATE_MODE

        self._init_extension()

        # Register views
        for view in self._views:
            app.register_blueprint(view.create_blueprint(self),
                                   register_with_babel=False)

        app.context_processor(
            lambda: dict(admin_base_template=self.base_template,
                         admin_view=self.index_view,
                         h=helpers,
                         get_url=url_for))
Example #14
0
    def register_routes(self, app: FlaskUnchained):
        redoc_url_prefix = app.config.get('API_REDOC_URL_PREFIX',
                                          '/api-docs').rstrip('/')
        template_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'templates')
        bp = Blueprint('api-docs',
                       __name__,
                       url_prefix=redoc_url_prefix,
                       template_folder=template_folder)

        # Serve json spec at 'url_prefix/openapi.json' by default
        json_path = app.config.get('OPENAPI_JSON_PATH', 'openapi.json')
        bp.add_url_rule(json_path,
                        endpoint='openapi_json',
                        view_func=self._openapi_json)

        # Serve ReDoc at `url_prefix/' by default
        redoc_path = app.config.get('OPENAPI_REDOC_PATH', '/')
        bp.add_url_rule(redoc_path,
                        endpoint='openapi_redoc',
                        view_func=self._openapi_redoc)

        app.register_blueprint(bp, register_with_babel=False)
Example #15
0
    def init_app(self, app: FlaskUnchained):
        # NOTE: the order of these `self.get_*` calls is important!
        self.confirm_serializer = self._get_serializer(app, 'confirm')
        self.hashing_context = self._get_hashing_context(app)
        self.login_manager = self._get_login_manager(
            app, app.config.SECURITY_ANONYMOUS_USER)
        self.login_serializer = self._get_serializer(app, 'login')
        self.principal = self._get_principal(app)
        self.pwd_context = self._get_pwd_context(app)
        self.remember_token_serializer = self._get_serializer(app, 'remember')
        self.reset_serializer = self._get_serializer(app, 'reset')

        self.context_processor(
            lambda: dict(security=_SecurityConfigProperties()))

        # FIXME: should this be easier to customize for end users, perhaps by making
        # FIXME: the function come from a config setting?
        identity_loaded.connect_via(app)(self._on_identity_loaded)
        app.extensions['security'] = self
Example #16
0
 def init_app(self, app: FlaskUnchained):
     app.extensions['mail'] = self
Example #17
0
 def add_url_rule(self, app: FlaskUnchained, rule: str, **kwargs):
     if app.config.ENABLE_URL_LANG_CODE_PREFIX:
         app.add_url_rule(self.get_url_rule(rule),
                          register_with_babel=False,
                          **kwargs)
Example #18
0
    def init_app(self, app: FlaskUnchained):
        self.app = app
        app.extensions['api'] = self

        self.spec = APISpec(app, plugins=app.config.API_APISPEC_PLUGINS)