logux-django

Django Logux integration engine https://logux.io/


License
MIT
Install
pip install logux-django==2.0

Documentation

Logux Django

Django Logux integration engine.

Logux Proto PyPI version Travis CI Lint and Test

Installation

Install from PyPI

pip install logux-django

Install dev version from current master.

pip install -e git://github.com/logux/django.git#egg=logux_django

Add path(r'logux/', include('logux.urls')), into your urls.py

Sets Logux settings in your settings.py:

# Logux settings: https://logux.io/guide/starting/proxy-server/
LOGUX_CONFIG = {
    'URL': 'http://localhost:31337/',
    'CONTROL_SECRET': 'parole',
    'AUTH_FUNC': auth_func,  # auth_func(user_id: str, token: str, cookie: dict, headers: dict) -> bool
    'SUBPROTOCOL': '1.0.0',
    'SUPPORTS': '^1.0.0'
}

Storing passwords or secrets in settings.py is bad practice. Use ENV.

For urls and settings examples, please checkout test_app settings

Keep in mind: the path in your urls.py (logux/) and the LOGUX_CONTROL_SECRET from the settings should be passed into Logux Server by ENV as LOGUX_BACKEND and LOGUX_CONTROL_SECRET respectively.

For example:

LOGUX_BACKEND=http://localhost:8000/logux/
LOGUX_CONTROL_SECRET=secret

Usage

Actions

For action handling add logux_actions.py file in your app, add ActionCommand inheritors and implement all his abstract methods.

Actions classes requirements:

  • Set action_type: str
  • Implement all ActionCommand abstracts methods
  • Implement resend and process methods if you need (optional)
  • import logux dispatcher: from logux.dispatchers import logux
  • Register all your action handlers: logux.actions.register(YourAction)

For example – User rename action handler:

import json
from typing import Optional, List

from logux.core import ActionCommand, Meta, Action
from logux.dispatchers import logux
from logux.exceptions import LoguxProxyException
from tests.test_app.models import User

class RenameUserAction(ActionCommand):
    """ During the subscription to users/USER_ID channel sends { type: "users/name", payload: { userId, name } }
    action with the latest user’s name. """
    action_type = 'users/name'

    def resend(self, action: Action, meta: Optional[Meta]) -> List[str]:
        return [f"users/{action['payload']['userId']}"]

    def access(self, action: Action, meta: Meta) -> bool:
        if 'error' in self.headers:
            raise LoguxProxyException(self.headers['error'])
        return action['payload']['userId'] == meta.user_id

    def process(self, action: Action, meta: Meta) -> None:
        user = User.objects.get(pk=action['payload']['userId'])
        first_name_meta = json.loads(user.first_name_meta)

        if not first_name_meta or Meta(first_name_meta).is_older(meta):
            user.first_name = action['payload']['name']
            user.first_name_meta = meta.get_json()
            user.save()


logux.actions.register(RenameUserAction)

Channels (Subscription)

For subsription handling add logux_subsriptions.py file in your app, and ChannelCommand inheritors and implement all his abstract methods.

Subscription classes requirements:

  • Set channel_pattern: str – this is a regexp like Django's url's patters in urls.py
  • Implement all ChannelCommand abstracts methods
  • import logux dispatcher: from logux.dispatchers import logux
  • Register all your subscription handlers: logux.channels.register(YourChannelCommand)

For example:

from typing import Optional

from logux.core import ChannelCommand, Action, Meta
from logux.dispatchers import logux
from logux.exceptions import LoguxProxyException
from tests.test_app.models import User


class UserChannel(ChannelCommand):

    channel_pattern = r'^users/(?P<user_id>\w+)$'
    
    def access(self, action: Action, meta: Meta) -> bool:
        return self.params['user_id'] == meta.user_id

    def load(self, action: Action, meta: Meta) -> Action:
        if 'error' in self.headers:
            raise LoguxProxyException(self.headers['error'])

        user, created = User.objects.get_or_create(id=self.params['user_id'])
        if created:
            user.first_name = 'Name'

        return {
            'type': 'users/name',
            'payload': {'userId': str(user.id), 'name': user.first_name}
        }


logux.channels.register(UserChannel)

For more examples, please checkout test app (tests/test_app)

Utils

logux.core.logux_add

logux_add(action: Action, raw_meta: Optional[Dict] = None) -> None is low level API function to send any actions and meta into Logux server.

If raw_meta is None just empty Dict will be passed to Logux server.

Keep in mind, in the current version logux_add is sync.

For more information: https://logux.io/node-api/#log-add

Development

Create dev environment

make venv
make install
make run

Type checking and linting:

make lint

Test:

make test

Integration tests (up server and run backend-test):

make integration_test

License

The package is available as open source under the terms of the MIT License.