Exemplo n.º 1
0
 def setUp(self):
     self.cache = MockRedisCacheAdapter()
     self.channel = 'channel1'
     self.stream = FilteredSseStream(self.channel, self.cache)
Exemplo n.º 2
0
class TestSimpleFilteredSseStream(TestCase):
    @classmethod
    def setUpClass(cls):
        patch_all()

    def setUp(self):
        self.cache = MockRedisCacheAdapter()
        self.channel = 'channel1'
        self.stream = FilteredSseStream(self.channel, self.cache)

    def tearDown(self):
        self.cache.clear()

    def test_init(self):
        self.assertEqual(self.stream.channel, self.channel)
        self.assertEqual(self.stream.cache, self.cache)

    def test_create_channel_name(self):
        self.assertEqual(self.stream.create_subchannel_name('a'),
                         '{}.a'.format(self.channel))
        self.assertEqual(self.stream.create_subchannel_name(14),
                         '{}.14'.format(self.channel))

    def assert_header_in_response(self, response, header, value):
        header_tuple = next(
            (header_ for header_ in response.headers if header_[0] == header),
            None)
        self.assertIsNotNone(header_tuple)
        self.assertEqual(header_tuple[1], value)

    def test_stream_default_headers(self):
        resp = self.stream.stream(subchannel='a')
        self.assert_header_in_response(resp, 'Connection', 'keep-alive')
        self.assert_header_in_response(resp, 'Cache-Control', 'no-cache')
        self.assert_header_in_response(resp, 'Content-Type',
                                       'text/event-stream; charset=utf-8')

    def test_stream_custom_headers(self):
        resp = self.stream.stream(subchannel='a',
                                  headers={
                                      'x-custom': 'yes',
                                      'Cache-Control': 'no-store'
                                  })
        self.assert_header_in_response(resp, 'Connection', 'keep-alive')
        self.assert_header_in_response(resp, 'Cache-Control', 'no-store')
        self.assert_header_in_response(resp, 'Content-Type',
                                       'text/event-stream; charset=utf-8')
        self.assert_header_in_response(resp, 'x-custom', 'yes')

    def test_send(self):
        @self.stream.push('event1')
        def pusher(a, ev, sub):
            gevent.sleep(0.1)
            return {'a': a}, sub, ev

        subs = ('aaa', 'bbb')

        result = {sub: [] for sub in subs}

        def listen(sub):
            for event in self.stream.send(subchannel=sub):
                result[sub].append(event)

        base_args = [('event1', 1), ('event2', 2)]
        args = {
            sub: [(event, data + i) for (event, data) in base_args]
            for i, sub in enumerate(subs)
        }

        def publish(sub):
            for event, data in args[sub]:
                pusher(data, event, sub)
            self.stream.unsubscribe(sub)

        sses = {
            sub: [SseEvent(event, {'a': arg}) for event, arg in args[sub]]
            for sub in subs
        }
        formatted_sses = {
            sub: [sse.format(i + 1) for i, sse in enumerate(sse_vals)]
            for sub, sse_vals in sses.items()
        }

        listen_threads = [gevent.spawn(listen, sub) for sub in subs]
        publish_threads = [gevent.spawn(publish, sub) for sub in subs]
        gevent.sleep(0.1)
        gevent.joinall(listen_threads, timeout=2)
        gevent.joinall(publish_threads, timeout=2)
        for sub in subs:
            self.assertListEqual(result[sub], formatted_sses[sub])

    def test_send_publish_multiple(self):

        subs = ('a', 'bbb')

        @self.stream.push('event1')
        def pusher(a, ev):
            gevent.sleep(0.1)
            return {'a': a}, subs, ev

        result = {sub: [] for sub in subs}

        def listen(sub):
            for event in self.stream.send(subchannel=sub):
                result[sub].append(event)

        base_args = [('event1', 1), ('event2', 2)]

        def publish():
            for event, data in base_args:
                pusher(data, event)
            for sub in subs:
                self.stream.unsubscribe(sub)

        sses = {
            sub: [SseEvent(event, {'a': arg}) for event, arg in base_args]
            for sub in subs
        }
        formatted_sses = {
            sub: [sse.format(i + 1) for i, sse in enumerate(sse_vals)]
            for sub, sse_vals in sses.items()
        }

        listen_threads = [gevent.spawn(listen, sub) for sub in subs]
        publish_thread = gevent.spawn(publish)
        gevent.sleep(0.1)
        gevent.joinall(listen_threads, timeout=2)
        publish_thread.join(timeout=2)
        for sub in subs:
            self.assertListEqual(result[sub], formatted_sses[sub])

    def test_send_with_retry(self):
        @self.stream.push('event1')
        def pusher(a, ev, sub):
            gevent.sleep(0.1)
            return {'a': a}, sub, ev

        subs = ('a', 'b')

        result = {'a': [], 'b': []}

        def listen(sub):
            for event in self.stream.send(subchannel=sub, retry=50):
                result[sub].append(event)

        base_args = [('event1', 1), ('event2', 2)]
        args = {
            sub: [(event, data + i) for (event, data) in base_args]
            for i, sub in enumerate(subs)
        }

        def publish(sub):
            for event, data in args[sub]:
                pusher(data, event, sub)
            self.stream.unsubscribe(sub)

        sses = {
            sub: [SseEvent(event, {'a': arg}) for event, arg in args[sub]]
            for sub in subs
        }
        formatted_sses = {
            sub:
            [sse.format(i + 1, retry=50) for i, sse in enumerate(sse_vals)]
            for sub, sse_vals in sses.items()
        }

        listen_threads = [gevent.spawn(listen, sub) for sub in subs]
        publish_threads = [gevent.spawn(publish, sub) for sub in subs]
        gevent.sleep(0.1)
        gevent.joinall(listen_threads, timeout=2)
        gevent.joinall(publish_threads, timeout=2)
        for sub in subs:
            self.assertListEqual(result[sub], formatted_sses[sub])
Exemplo n.º 3
0
from datetime import datetime

from enum import Enum, unique
from flask_jwt_extended import get_jwt_identity

from walkoff.messaging import MessageActionEvent
from walkoff.security import jwt_required_in_query
from walkoff.sse import FilteredSseStream, StreamableBlueprint

sse_stream = FilteredSseStream('notifications')

notifications_page = StreamableBlueprint('notifications_page',
                                         __name__,
                                         streams=[sse_stream])


@unique
class NotificationSseEvent(Enum):
    created = 1
    read = 2
    responded = 3


def format_read_responded_data(message, user):
    return {
        'id': message.id,
        'username': user.username,
        'timestamp': datetime.utcnow().isoformat()
    }

Exemplo n.º 4
0
from datetime import datetime
from uuid import UUID

from enum import Enum, unique
from flask import current_app, request

from walkoff.events import WalkoffEvent
from walkoff.executiondb import ActionStatusEnum, WorkflowStatusEnum
from walkoff.executiondb.workflowresults import WorkflowStatus
from walkoff.helpers import convert_action_argument, utc_as_rfc_datetime
from walkoff.security import jwt_required_in_query
from walkoff.server.problem import Problem
from walkoff.server.returncodes import BAD_REQUEST
from walkoff.sse import FilteredSseStream, StreamableBlueprint

workflow_stream = FilteredSseStream('workflow_results')
action_stream = FilteredSseStream('action_results')
action_summary_stream = FilteredSseStream('action_results_summary')

workflowresults_page = StreamableBlueprint(
    'workflowresults_page',
    __name__,
    streams=(workflow_stream, action_stream, action_summary_stream)
)

action_summary_keys = ('action_name', 'app_name', 'action_id', 'name', 'timestamp', 'workflow_execution_id')


@unique
class ActionStreamEvent(Enum):
    started = 1
Exemplo n.º 5
0
import logging
from uuid import UUID

from flask import request

from walkoff.events import WalkoffEvent
from walkoff.security import jwt_required_in_query
from walkoff.sse import FilteredSseStream, StreamableBlueprint
from walkoff.server.problem import Problem
from walkoff.server.returncodes import BAD_REQUEST

console_stream = FilteredSseStream('console_results')
console_page = StreamableBlueprint('console_page', __name__, streams=(console_stream,))


def format_console_data(sender, data):
    try:
        level = int(data['level'])
    except ValueError:
        level = data['level']
    return {
        'workflow': sender['name'],
        'app_name': data['app_name'],
        'action_name': data['action_name'],
        'level':  logging.getLevelName(level),
        'message': data['message']
    }


@WalkoffEvent.ConsoleLog.connect
@console_stream.push('log')
Exemplo n.º 6
0
class TestSimpleFilteredSseStream(TestCase):
    @classmethod
    def setUpClass(cls):
        patch_all()

    def setUp(self):
        self.cache = MockRedisCacheAdapter()
        self.channel = 'channel1'
        self.stream = FilteredSseStream(self.channel, self.cache)

    def tearDown(self):
        self.cache.clear()

    def test_init(self):
        self.assertEqual(self.stream.channel, self.channel)
        self.assertEqual(self.stream.cache, self.cache)

    def test_create_channel_name(self):
        self.assertEqual(self.stream.create_subchannel_name('a'), '{}.a'.format(self.channel))
        self.assertEqual(self.stream.create_subchannel_name(14), '{}.14'.format(self.channel))

    def assert_header_in_response(self, response, header, value):
        header_tuple = next((header_ for header_ in response.headers if header_[0] == header), None)
        self.assertIsNotNone(header_tuple)
        self.assertEqual(header_tuple[1], value)

    def test_stream_default_headers(self):
        resp = self.stream.stream(subchannel='a')
        self.assert_header_in_response(resp, 'Connection', 'keep-alive')
        self.assert_header_in_response(resp, 'Cache-Control', 'no-cache')
        self.assert_header_in_response(resp, 'Content-Type', 'text/event-stream; charset=utf-8')

    def test_stream_custom_headers(self):
        resp = self.stream.stream(subchannel='a', headers={'x-custom': 'yes', 'Cache-Control': 'no-store'})
        self.assert_header_in_response(resp, 'Connection', 'keep-alive')
        self.assert_header_in_response(resp, 'Cache-Control', 'no-store')
        self.assert_header_in_response(resp, 'Content-Type', 'text/event-stream; charset=utf-8')
        self.assert_header_in_response(resp, 'x-custom', 'yes')

    def test_send(self):

        @self.stream.push('event1')
        def pusher(a, ev, sub):
            gevent.sleep(0.1)
            return {'a': a}, sub, ev

        subs = ('aaa', 'bbb')

        result = {sub: [] for sub in subs}

        def listen(sub):
            for event in self.stream.send(subchannel=sub):
                result[sub].append(event)

        base_args = [('event1', 1), ('event2', 2)]
        args = {sub: [(event, data + i) for (event, data) in base_args] for i, sub in enumerate(subs)}

        def publish(sub):
            for event, data in args[sub]:
                pusher(data, event, sub)
            self.stream.unsubscribe(sub)

        sses = {sub: [SseEvent(event, {'a': arg}) for event, arg in args[sub]] for sub in subs}
        formatted_sses = {sub: [sse.format(i + 1) for i, sse in enumerate(sse_vals)] for sub, sse_vals in sses.items()}

        listen_threads = [gevent.spawn(listen, sub) for sub in subs]
        publish_threads = [gevent.spawn(publish, sub) for sub in subs]
        gevent.sleep(0.1)
        gevent.joinall(listen_threads, timeout=2)
        gevent.joinall(publish_threads, timeout=2)
        for sub in subs:
            self.assertListEqual(result[sub], formatted_sses[sub])

    def test_send_publish_multiple(self):

        subs = ('a', 'bbb')

        @self.stream.push('event1')
        def pusher(a, ev):
            gevent.sleep(0.1)
            return {'a': a}, subs, ev

        result = {sub: [] for sub in subs}

        def listen(sub):
            for event in self.stream.send(subchannel=sub):
                result[sub].append(event)

        base_args = [('event1', 1), ('event2', 2)]

        def publish():
            for event, data in base_args:
                pusher(data, event)
            for sub in subs:
                self.stream.unsubscribe(sub)

        sses = {sub: [SseEvent(event, {'a': arg}) for event, arg in base_args] for sub in subs}
        formatted_sses = {sub: [sse.format(i + 1) for i, sse in enumerate(sse_vals)] for sub, sse_vals in sses.items()}

        listen_threads = [gevent.spawn(listen, sub) for sub in subs]
        publish_thread = gevent.spawn(publish)
        gevent.sleep(0.1)
        gevent.joinall(listen_threads, timeout=2)
        publish_thread.join(timeout=2)
        for sub in subs:
            self.assertListEqual(result[sub], formatted_sses[sub])

    def test_send_with_retry(self):

        @self.stream.push('event1')
        def pusher(a, ev, sub):
            gevent.sleep(0.1)
            return {'a': a}, sub, ev

        subs = ('a', 'b')

        result = {'a': [], 'b': []}

        def listen(sub):
            for event in self.stream.send(subchannel=sub, retry=50):
                result[sub].append(event)

        base_args = [('event1', 1), ('event2', 2)]
        args = {sub: [(event, data + i) for (event, data) in base_args] for i, sub in enumerate(subs)}

        def publish(sub):
            for event, data in args[sub]:
                pusher(data, event, sub)
            self.stream.unsubscribe(sub)

        sses = {sub: [SseEvent(event, {'a': arg}) for event, arg in args[sub]] for sub in subs}
        formatted_sses = {sub: [sse.format(i + 1, retry=50) for i, sse in enumerate(sse_vals)] for sub, sse_vals in
                          sses.items()}

        listen_threads = [gevent.spawn(listen, sub) for sub in subs]
        publish_threads = [gevent.spawn(publish, sub) for sub in subs]
        gevent.sleep(0.1)
        gevent.joinall(listen_threads, timeout=2)
        gevent.joinall(publish_threads, timeout=2)
        for sub in subs:
            self.assertListEqual(result[sub], formatted_sses[sub])
Exemplo n.º 7
0
 def setUp(self):
     self.cache = MockRedisCacheAdapter()
     self.channel = 'channel1'
     self.stream = FilteredSseStream(self.channel, self.cache)