Пример #1
0
    def test_replace(self):
        """replace method adds function to patch object"""
        patch = ApiPatch()

        def mock_function():
            pass

        patch.replace('test-replace', mock_function)

        self.assertEqual(len(patch._actions), 1)
        self.assertEqual(patch._actions[0]['op'], 'replace')
        self.assertEqual(patch._actions[0]['path'], 'test-replace')
        self.assertEqual(patch._actions[0]['handler'], mock_function)
Пример #2
0
    def test_add(self):
        """add method adds function to patch object"""
        patch = ApiPatch()

        def mock_function():
            pass

        patch.add('test-add', mock_function)

        self.assertEqual(len(patch._actions), 1)
        self.assertEqual(patch._actions[0]['op'], 'add')
        self.assertEqual(patch._actions[0]['path'], 'test-add')
        self.assertEqual(patch._actions[0]['handler'], mock_function)
Пример #3
0
from misago.threads.participants import (add_participant, change_owner,
                                         make_participants_aware,
                                         remove_participant)
from misago.threads.permissions import (
    allow_add_participant, allow_add_participants, allow_approve_thread,
    allow_change_owner, allow_edit_thread, allow_pin_thread, allow_hide_thread,
    allow_move_thread, allow_remove_participant, allow_start_thread,
    allow_unhide_thread)
from misago.threads.serializers import ThreadParticipantSerializer
from misago.threads.validators import validate_title

PATCH_LIMIT = settings.MISAGO_THREADS_PER_PAGE + settings.MISAGO_THREADS_TAIL

UserModel = get_user_model()

thread_patch_dispatcher = ApiPatch()


def patch_acl(request, thread, value):
    """useful little op that updates thread acl to current state"""
    if value:
        add_acl(request.user, thread)
        return {'acl': thread.acl}
    else:
        return {'acl': None}


thread_patch_dispatcher.add('acl', patch_acl)


def patch_title(request, thread, value):
Пример #4
0
from django.utils.translation import ugettext as _

from misago.acl import add_acl
from misago.api.patch import ApiPatch
from misago.conf import settings
from misago.threads.models import PostLike
from misago.threads import moderation
from misago.threads.permissions import (
    allow_approve_post, allow_hide_best_answer, allow_hide_post, allow_protect_post,
    allow_unhide_post)
from misago.threads.permissions import exclude_invisible_posts


PATCH_LIMIT = settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL

post_patch_dispatcher = ApiPatch()


def patch_acl(request, post, value):
    """useful little op that updates post acl to current state"""
    if value:
        add_acl(request.user, post)
        return {'acl': post.acl}
    else:
        return {'acl': None}


post_patch_dispatcher.add('acl', patch_acl)


def patch_is_liked(request, post, value):
Пример #5
0
    def test_dispatch_bulk(self):
        """dispatch_bulk calls actions and returns response"""
        patch = ApiPatch()

        def action_error(request, target, value):
            if value == '404':
                raise Http404()
            if value == '404_reason':
                raise Http404("something was removed")
            if value == 'perm':
                raise PermissionDenied("yo ain't doing that!")
            if value == 'invalid':
                raise ValidationError("invalid data here!")
            if value == 'api_invalid':
                raise ApiValidationError("invalid api data here!")

        patch.replace('error', action_error)

        def action_mutate(request, target, value):
            return {'value': value * 2}

        patch.replace('mutate', action_mutate)

        # valid bulk dispatch
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '200',
                'patch': {
                    'value': 14
                }
            },
            {
                'id': '7',
                'status': '200',
                'patch': {
                    'value': 14
                }
            },
        ])

        # invalid action in bulk dispatch
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data, {
            'detail': '"replace" op has to specify path.',
        })

        # op raised validation error
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'invalid',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '400',
                'detail': ["invalid data here!"]
            },
            {
                'id': '7',
                'status': '400',
                'detail': ["invalid data here!"]
            },
        ])

        # op raised api validation error
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'api_invalid',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '400',
                'detail': ["invalid api data here!"]
            },
            {
                'id': '7',
                'status': '400',
                'detail': ["invalid api data here!"]
            },
        ])

        # action in bulk dispatch raised perm denied
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 9,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'perm',
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '403',
                'detail': "yo ain't doing that!"
            },
            {
                'id': '7',
                'status': '403',
                'detail': "yo ain't doing that!"
            },
        ])

        # action in bulk dispatch raised 404
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': '404',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '404',
                'detail': "NOT FOUND"
            },
            {
                'id': '7',
                'status': '404',
                'detail': "NOT FOUND"
            },
        ])

        # action in bulk dispatch raised 404 with message
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': '404_reason',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '404',
                'detail': "something was removed"
            },
            {
                'id': '7',
                'status': '404',
                'detail': "something was removed"
            },
        ])
Пример #6
0
    def test_dispatch(self):
        """dispatch calls actions and returns response"""
        patch = ApiPatch()

        def action_error(request, target, value):
            if value == '404':
                raise Http404()
            if value == '404_reason':
                raise Http404("something was removed")
            if value == 'perm':
                raise PermissionDenied("yo ain't doing that!")
            if value == 'invalid':
                raise ValidationError("invalid data here!")
            if value == 'api_invalid':
                raise ApiValidationError("invalid api data here!")

        patch.replace('error', action_error)

        def action_mutate(request, target, value):
            return {'value': value * 2}

        patch.replace('mutate', action_mutate)

        # dispatch requires list as an argument
        response = patch.dispatch(MockRequest({}), {})
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data['detail'],
                         "PATCH request should be a list of operations.")

        # valid dispatch
        response = patch.dispatch(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]), MockObject(13))

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, {'value': 14, 'id': 13})

        # invalid action in dispatch
        response = patch.dispatch(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]), MockObject(13))

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data['detail'],
                         '"replace" op has to specify path.')

        # op raised validation error
        response = patch.dispatch(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'invalid',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]), MockObject(13))

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data['detail'], ["invalid data here!"])

        # op raised api validation error
        response = patch.dispatch(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'api_invalid',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]), MockObject(13))

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data['detail'], ["invalid api data here!"])

        # action in dispatch raised perm denied
        response = patch.dispatch(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 9,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'perm',
                },
            ]), MockObject(13))

        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.data['detail'], "yo ain't doing that!")

        # action in dispatch raised 404
        response = patch.dispatch(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': '404',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]), MockObject(13))

        self.assertEqual(response.status_code, 404)
        self.assertEqual(response.data['detail'], "NOT FOUND")

        # action in dispatch raised 404 with message
        response = patch.dispatch(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': '404_reason',
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 7,
                },
            ]), MockObject(13))

        self.assertEqual(response.status_code, 404)
        self.assertEqual(response.data['detail'], "something was removed")
Пример #7
0
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext as _

from misago.acl import add_acl
from misago.api.patch import ApiPatch
from misago.threads.moderation import posts as moderation
from misago.threads.permissions import allow_hide_event, allow_unhide_event

event_patch_dispatcher = ApiPatch()


def patch_acl(request, event, value):
    """useful little op that updates event acl to current state"""
    if value:
        add_acl(request.user, event)
        return {'acl': event.acl}
    else:
        return {'acl': None}


event_patch_dispatcher.add('acl', patch_acl)


def patch_is_hidden(request, event, value):
    if value:
        allow_hide_event(request.user, event)
        moderation.hide_post(request.user, event)
    else:
        allow_unhide_event(request.user, event)
        moderation.unhide_post(request.user, event)
Пример #8
0
    def test_validate_action(self):
        """validate_action method validates action dict"""
        patch = ApiPatch()

        VALID_ACTIONS = [
            {
                'op': 'add',
                'path': 'test',
                'value': 42
            },
            {
                'op': 'remove',
                'path': 'other-test',
                'value': 'Lorem'
            },
            {
                'op': 'replace',
                'path': 'false-test',
                'value': None
            },
        ]

        for action in VALID_ACTIONS:
            patch.validate_action(action)

        # undefined op
        UNSUPPORTED_ACTIONS = ({}, {'op': ''}, {'no': 'op'}, )

        for action in UNSUPPORTED_ACTIONS:
            try:
                patch.validate_action(action)
            except InvalidAction as e:
                self.assertEqual(e.args[0], '"op" parameter must be defined.')

        # unsupported op
        try:
            patch.validate_action({'op': 'nope'})
        except InvalidAction as e:
            self.assertEqual(e.args[0], u'"nope" op is unsupported.')

        # op lacking patch
        try:
            patch.validate_action({'op': 'add'})
        except InvalidAction as e:
            self.assertEqual(e.args[0], u'"add" op has to specify path.')

        # op lacking value
        try:
            patch.validate_action({
                'op': 'add',
                'path': 'yolo',
            })
        except InvalidAction as e:
            self.assertEqual(e.args[0], u'"add" op has to specify value.')

        # empty value is allowed
        try:
            patch.validate_action({
                'op': 'add',
                'path': 'yolo',
                'value': '',
            })
        except InvalidAction as e:
            self.assertEqual(e.args[0], u'"add" op has to specify value.')
Пример #9
0
    def test_dispatch_action(self):
        """dispatch_action calls specified actions"""
        patch = ApiPatch()

        mock_target = MockObject(13)

        def action_a(request, target, value):
            self.assertEqual(request, 'request')
            self.assertEqual(target, mock_target)
            return {'a': value * 2, 'b': 111}

        patch.replace('abc', action_a)

        def action_b(request, target, value):
            self.assertEqual(request, 'request')
            self.assertEqual(target, mock_target)
            return {'b': value * 10}

        patch.replace('abc', action_b)

        def action_fail(request, target, value):
            self.fail("unrequired action was called")

        patch.add('c', action_fail)
        patch.remove('c', action_fail)
        patch.replace('c', action_fail)

        patch_dict = {'id': 123}

        patch.dispatch_action(
            patch_dict, 'request', mock_target, {
                'op': 'replace',
                'path': 'abc',
                'value': 5,
            }
        )

        self.assertEqual(len(patch_dict), 3)
        self.assertEqual(patch_dict['id'], 123)
        self.assertEqual(patch_dict['a'], 10)
        self.assertEqual(patch_dict['b'], 50)
Пример #10
0
    def test_validate_actions(self):
        """validate_actions method validates action dict"""
        patch = ApiPatch()

        VALID_ACTIONS = [
            {
                'op': 'add',
                'path': 'test',
                'value': 42
            },
            {
                'op': 'remove',
                'path': 'other-test',
                'value': 'Lorem'
            },
            {
                'op': 'replace',
                'path': 'false-test',
                'value': None
            },
        ]

        for action in VALID_ACTIONS:
            patch.validate_actions([action])

        # undefined op
        UNSUPPORTED_ACTIONS = (
            {},
            {
                'op': ''
            },
            {
                'no': 'op'
            },
        )

        for action in UNSUPPORTED_ACTIONS:
            try:
                patch.validate_actions([action])
            except InvalidAction as e:
                self.assertEqual(e.args[0], '"op" parameter must be defined.')

        # unsupported op
        try:
            patch.validate_actions([{'op': 'nope'}])
        except InvalidAction as e:
            self.assertEqual(e.args[0], '"nope" op is unsupported.')

        # op lacking patch
        try:
            patch.validate_actions([{'op': 'add'}])
        except InvalidAction as e:
            self.assertEqual(e.args[0], '"add" op has to specify path.')

        # op lacking value
        try:
            patch.validate_actions([{
                'op': 'add',
                'path': 'yolo',
            }])
        except InvalidAction as e:
            self.assertEqual(e.args[0], '"add" op has to specify value.')

        # empty value is forbidden
        try:
            patch.validate_actions([{
                'op': 'add',
                'path': 'yolo',
                'value': '',
            }])
        except InvalidAction as e:
            self.assertEqual(e.args[0], '"add" op has to specify value.')

        # duplicated actions are forbidden
        try:
            patch.validate_actions([
                {
                    'op': 'add',
                    'path': 'like',
                    'value': True
                },
                {
                    'op': 'add',
                    'path': 'like',
                    'value': False
                },
            ])
        except InvalidAction as e:
            self.assertEqual(e.args[0],
                             '"add" op for "like" path is repeated.')
Пример #11
0
    def test_dispatch_bulk(self):
        """dispatch_bulk calls actions and returns response"""
        patch = ApiPatch()

        def action_error(request, target, value):
            if value == '404':
                raise Http404()
            if value == '404_reason':
                raise Http404("something was removed")
            if value == 'perm':
                raise PermissionDenied("yo ain't doing that!")
            if value == 'invalid':
                raise ValidationError("invalid data here!")
            if value == 'api_invalid':
                raise ApiValidationError("invalid api data here!")

        patch.replace('error', action_error)

        def action_mutate(request, target, value):
            return {'value': value * 2}

        patch.replace('mutate', action_mutate)

        # valid bulk dispatch
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '200',
                'patch': {
                    'value': 12
                }
            },
            {
                'id': '7',
                'status': '200',
                'patch': {
                    'value': 12
                }
            },
        ])

        # dispatch requires list as an argument
        response = patch.dispatch_bulk(MockRequest(
            {}), [MockObject(5), MockObject(7)])
        self.assertEqual(response.status_code, 400)
        self.assertEqual(
            response.data, {
                'non_field_errors':
                ["PATCH request should be a list of operations."],
            })

        # invalid action in bulk dispatch
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data, {
            'non_field_errors': ['"replace" op has to specify path.'],
        })

        # repeated action in dispatch
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 12,
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data, {
            'non_field_errors':
            ['"replace" op for "mutate" path is repeated.'],
        })

        # op raised validation error
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'invalid',
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '400',
                'value': ["invalid data here!"]
            },
            {
                'id': '7',
                'status': '400',
                'value': ["invalid data here!"]
            },
        ])

        # op raised api validation error
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'api_invalid',
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '400',
                'value': ["invalid api data here!"]
            },
            {
                'id': '7',
                'status': '400',
                'value': ["invalid api data here!"]
            },
        ])

        # action in bulk dispatch raised perm denied
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': 'perm',
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '403',
                'detail': "yo ain't doing that!"
            },
            {
                'id': '7',
                'status': '403',
                'detail': "yo ain't doing that!"
            },
        ])

        # action in bulk dispatch raised 404
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 6,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': '404',
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '404',
                'detail': 'NOT FOUND'
            },
            {
                'id': '7',
                'status': '404',
                'detail': 'NOT FOUND'
            },
        ])

        # action in dispatch raised 404 with message but didn't expose it
        response = patch.dispatch_bulk(
            MockRequest([
                {
                    'op': 'replace',
                    'path': 'mutate',
                    'value': 2,
                },
                {
                    'op': 'replace',
                    'path': 'error',
                    'value': '404_reason',
                },
            ]),
            [MockObject(5), MockObject(7)],
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [
            {
                'id': '5',
                'status': '404',
                'detail': 'NOT FOUND'
            },
            {
                'id': '7',
                'status': '404',
                'detail': 'NOT FOUND'
            },
        ])