Git Product home page Git Product logo

mara-page's Introduction

Mara page

mara-page PyPI - License PyPI version Slack Status

Minimal API for defining pages of Flask-based backends independently from the actual backend infrastructure.

When a web app is spread across many independent Flask blueprints, then this library can be used to add

  • navigation entries
  • page titles
  • resource-based ACL protection
  • page-specific CSS and JS files

without having access to a global layout or the Flask app.

The library provides a drop-in werkzeug Response class that is enriched with additional information that a backend can use to render the final html page.

Example

This is a simple web ui for displaying the current time:

"""Clock UI"""

import flask
from awesome_clock import clock
from mara_page import acl, navigation, response, bootstrap, _

# The flask blueprint that handles
blueprint = flask.Blueprint('awesome_clock', __name__, url_prefix='/clock', static_folder='static')

# Defines an ACL resource (needs to be handled by the application)
acl_resource = acl.AclResource('Clock')


def navigation_entry():
    """Defines a part of the navigation tree (needs to be handled by the application)"""
    return navigation.NavigationEntry(
        label='Awesome clock', icon='clock-o', description='Something that tells the time',
        children=[
            navigation.NavigationEntry(
                label='What time is it now?',
                uri_fn=lambda: flask.url_for('awesome_clock.clock_page', when='now'),
                icon='question-circle', description='Should be able to display the current time'),

            navigation.NavigationEntry(
                label='And now?', icon='refresh', description='For the impatient',
                uri_fn=lambda: flask.url_for('awesome_clock.clock_page', when='and-now'))
        ])


@blueprint.route('/<string:when>')
@acl.require_permission(acl_resource)  # Requires permission to the `'Clock'` resource
def clock_page(when: str):
    """Defines the `/clock` page"""
    return response.Response(
        # The actual page content (can be also a call to `flask.render_template`
        # or anything else that produces a string)
        html=bootstrap.card(header_left=_.h1['What time is it now?'],
                            body=[_.p['The current time is ', str(clock.what_time_is_it_now())],
                                  _.img(src=flask.url_for('awesome_clock.static', filename='cuckoo-clock.jpg'),
                                        style='max-width:100%')]),
        # The page title
        title='Awesome clock',

        # Action buttons
        action_buttons=[
            response.ActionButton('javascript:location.reload()', 'Refresh', 'Refresh clock', 'refresh'),
            response.ActionButton('javascript:location.reload()', 'Update', 'Refresh clock', 'clock-o')
        ]
    )

It is up to the actual Flask app to define how to render such a response and what to do with the ACL resources and navigation entries. The mara app will render the response like this:

Example backend

Links

mara-page's People

Contributors

gathineou avatar hendrikmakait avatar jankatins avatar leo-schick avatar martin-loetzsch avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mara-page's Issues

Add custom message to require_permission()

Currently it always says "You don't have enough permissions to view this page.", it would be nice to add a custom message there so I could add that to Buttons: "You don't have enough permissions to do XYZ."

Hiding navigation entries

This unfortunately, does not work:

from mara_page import navigation
import flask
from mara_page import acl

cache_control_acl_resource = acl.AclResource('Cache Control')
admin_acl_ressource.add_child(cache_control_acl_resource)

if acl.current_user_has_permission(cache_control_acl_resource):
    navigation_entry = navigation.NavigationEntry(
        label='Flush', uri_fn=lambda: flask.url_for('cache_page.start'), icon='refresh', rank=10,
        description='App Caching')

Error:

File "/home/js/projects/pricing/app/__init__.py", line 15, in <module>
import app.ui
File "/home/js/projects/pricing/app/ui/__init__.py", line 4, in <module>
import app.ui.changes_overview_page
File "/home/js/projects/pricing/app/ui/changes_overview_page.py", line 5, in <module>
from .cache import get_cache
File "/home/js/projects/pricing/app/ui/cache.py", line 50, in <module>
if acl.current_user_has_permission(cache_control_acl_resource):
File "/home/js/projects/pricing/packages/mara-acl/mara_acl/permissions.py", line 36, in current_user_has_permission
+ "' LIKE CONCAT(user_key,'%'))").scalar():
File "/home/js/projects/pricing/.venv/lib/python3.6/site-packages/werkzeug/local.py", line 347, in __getattr__
return getattr(self._get_current_object(), name)
File "/home/js/projects/pricing/.venv/lib/python3.6/site-packages/werkzeug/local.py", line 306, in _get_current_object
return self.__local()
File "/home/js/projects/pricing/.venv/lib/python3.6/site-packages/flask/globals.py", line 44, in _lookup_app_object
raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context. This typically means that you attempted to use functionality that needed to interface with the current application object in a way. To solve this set up an application context with app.app_context(). See the documentation for more information.

Using mara_page without mara_acl and without patching permissions leads to TypeError

There seems to be an issue in mara_page/acl.py, where if you don't also use mara_acl or patch current_user_has_permissions you'll get a TypeError: 'map' object is not subscriptable:

...
[2020-10-14 12:16:20,247] ERROR in app: Exception on /pipelines/dependency-graph [GET]
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/mara/app/src/mara-page/mara_page/acl.py", line 101, in wrapper
    if not current_user_has_permission(resource):
  File "/mara/app/src/mara-page/mara_page/acl.py", line 47, in current_user_has_permission
    return current_user_has_permissions([resource])[0][1]
TypeError: 'map' object is not subscriptable
172.19.0.1 - - [14/Oct/2020 12:16:20] "GET /pipelines/dependency-graph HTTP/1.1" 500 -
...

I "fixed" it locally by changing current_user_has_permissions to

def current_user_has_permissions(resources: [AclResource]) -> [[AclResource, bool]]:
    """
    Determines whether the currently logged in user has permissions for a list of resources.
    Implement actual behavior by patching the function.
    """
    return list(map(lambda resource: [resource, True], resources))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.