Skip to content
This repository has been archived by the owner on Aug 25, 2024. It is now read-only.
/ django Public archive

Django Logux integration engine

License

Notifications You must be signed in to change notification settings

logux/django

Repository files navigation

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.org/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.org/node-api/#log-add

Development

We use Poetry and dephell for dealing with deps.

Create dev environment, setup logux in develop mode, run local test server

make deps
make install
make run

Type checking and linting:

make lint

Test:

make test

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

Install backend-test deps:

make lbt_deps

Run integration tests

make integration_test

License

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