Ejemplo n.º 1
0
    def recover_step(self, message: dict):
        ''' given an execution id and a pointer from the persistent storage,
        return the asociated process node to continue its execution '''
        try:
            pointer = Pointer.get_or_exception(message['pointer_id'])
            if pointer.status != 'ongoing':
                raise ModelNotFoundError(
                    'Specified pointer never existed, and never will', )

        except ModelNotFoundError:
            raise InconsistentState('Queued dead pointer')

        user = User.get_by('identifier', message.get('user_identifier'))

        if user is None:
            if message.get('user_identifier') == '__system__':
                user = User(identifier='__system__', fullname='System').save()
            else:
                raise InconsistentState('sent identifier of unexisten user')

        return (
            pointer,
            user,
            message['input'],
        )
Ejemplo n.º 2
0
def get_or_create(identifier, data):
    identifier = clear_username(identifier)
    data['identifier'] = identifier

    try:
        return User.get_by_or_exception('identifier', identifier)
    except ModelNotFoundError:
        return User(**data).save()
Ejemplo n.º 3
0
def test_backref_backend(config):
    user = User(identifier='juan').save()
    br = BackrefHierarchyProvider(config)

    users = br.find_users(identifier='juan')

    assert len(users) == 1

    user = users[0]

    assert user[0] == 'juan'
    assert user[1] == {
        'identifier': 'juan',
        'email': 'juan',
        'fullname': 'juan',
    }
Ejemplo n.º 4
0
def make_user(identifier, name):
    u = User(identifier=identifier, fullname=name).save()
    token = Token(token=random_string(9)).save()
    token.proxy.user.set(u)

    return u
Ejemplo n.º 5
0
def test_wakeup(config, mongo):
    ''' the first stage in a node's lifecycle '''
    # setup stuff
    handler = Handler(config)

    pointer = make_pointer('simple.2018-02-19.xml', 'start_node')
    execution = pointer.proxy.execution.get()
    juan = User(identifier='juan').save()
    manager = User(
        identifier='juan_manager',
        email='*****@*****.**'
    ).save()

    mongo[config["EXECUTION_COLLECTION"]].insert_one({
        '_type': 'execution',
        'id': execution.id,
        'state': Xml.load(config, execution.process_name).get_state(),
        'actors': {'start_node': 'juan'},
    })

    channel = MagicMock()

    # will wakeup the second node
    handler.call({
        'command': 'step',
        'pointer_id': pointer.id,
        'user_identifier': juan.identifier,
        'input': [],
    }, channel)

    # test manager is notified
    channel.basic_publish.assert_called_once()
    channel.exchange_declare.assert_called_once()

    args = channel.basic_publish.call_args[1]

    assert args['exchange'] == config['RABBIT_NOTIFY_EXCHANGE']
    assert args['routing_key'] == 'email'
    assert json.loads(args['body']) == {
        'recipient': '*****@*****.**',
        'subject': '[procesos] Tarea asignada',
        'template': 'assigned-task.html',
        'data': {
            'pointer': Pointer.get_all()[0].to_json(
                include=['*', 'execution']
            ),
            'cacahuate_url': config['GUI_URL'],
        },
    }

    # pointer collection updated
    reg = next(mongo[config["POINTER_COLLECTION"]].find())

    assert_near_date(reg['started_at'])
    assert reg['finished_at'] is None
    assert reg['execution']['id'] == execution.id
    assert reg['node'] == {
        'id': 'mid_node',
        'type': 'action',
        'description': 'añadir información',
        'name': 'Segundo paso',
    }
    assert reg['actors'] == {
        '_type': ':map',
        'items': {},
    }
    assert reg['notified_users'] == [manager.to_json()]
    assert reg['state'] == 'ongoing'

    # execution collection updated
    reg = next(mongo[config["EXECUTION_COLLECTION"]].find())

    assert reg['state']['items']['mid_node']['state'] == 'ongoing'

    # tasks where asigned
    assert manager.proxy.tasks.count() == 1

    task = manager.proxy.tasks.get()[0]

    assert isinstance(task, Pointer)
    assert task.node_id == 'mid_node'
    assert task.proxy.execution.get().id == execution.id
Ejemplo n.º 6
0
def test_teardown(config, mongo):
    ''' second and last stage of a node's lifecycle '''
    # test setup
    handler = Handler(config)

    p_0 = make_pointer('simple.2018-02-19.xml', 'mid_node')
    execution = p_0.proxy.execution.get()

    User(identifier='juan').save()
    manager = User(identifier='manager').save()
    manager2 = User(identifier='manager2').save()

    assert manager not in execution.proxy.actors.get()
    assert execution not in manager.proxy.activities.get()

    manager.proxy.tasks.set([p_0])
    manager2.proxy.tasks.set([p_0])

    state = Xml.load(config, execution.process_name).get_state()
    state['items']['start_node']['state'] = 'valid'

    mongo[config["EXECUTION_COLLECTION"]].insert_one({
        '_type': 'execution',
        'id': execution.id,
        'state': state,
        'values': {
            '_execution': [{
                'name': '',
                'description': '',
            }],
        },
        'actors': {
            'start_node': 'juan',
        },
    })

    mongo[config["POINTER_COLLECTION"]].insert_one({
        'id': p_0.id,
        'started_at': datetime(2018, 4, 1, 21, 45),
        'finished_at': None,
        'execution': {
            'id': execution.id,
        },
        'node': {
            'id': p_0.node_id,
        },
        'actors': {
            '_type': ':map',
            'items': {},
        },
    })

    channel = MagicMock()

    # will teardown mid_node
    handler.call({
        'command': 'step',
        'pointer_id': p_0.id,
        'user_identifier': manager.identifier,
        'input': [Form.state_json('mid_form', [
            {
                '_type': 'field',
                'state': 'valid',
                'value': 'yes',
                'value_caption': 'yes',
                'name': 'data',
            },
        ])],
    }, channel)

    # assertions
    assert Pointer.get(p_0.id) is None

    assert Pointer.count() == 1
    assert Pointer.get_all()[0].node_id == 'final_node'

    # mongo has a registry
    reg = next(mongo[config["POINTER_COLLECTION"]].find())

    assert reg['started_at'] == datetime(2018, 4, 1, 21, 45)
    assert_near_date(reg['finished_at'])
    assert reg['execution']['id'] == execution.id
    assert reg['node']['id'] == p_0.node_id
    assert reg['actors'] == {
        '_type': ':map',
        'items': {
            'manager': {
                '_type': 'actor',
                'state': 'valid',
                'user': {
                    '_type': 'user',
                    'identifier': 'manager',
                    'fullname': None,
                },
                'forms': [Form.state_json('mid_form', [
                    {
                        '_type': 'field',
                        'state': 'valid',
                        'value': 'yes',
                        'value_caption': 'yes',
                        'name': 'data',
                    },
                ])],
            },
        },
    }

    # tasks where deleted from user
    assert manager.proxy.tasks.count() == 0
    assert manager2.proxy.tasks.count() == 0

    # state
    reg = next(mongo[config["EXECUTION_COLLECTION"]].find())

    assert reg['state'] == {
        '_type': ':sorted_map',
        'items': {
            'start_node': {
                '_type': 'node',
                'type': 'action',
                'id': 'start_node',
                'state': 'valid',
                'comment': '',
                'actors': {
                    '_type': ':map',
                    'items': {},
                },
                'milestone': False,
                'name': 'Primer paso',
                'description': 'Resolver una tarea',
            },

            'mid_node': {
                '_type': 'node',
                'type': 'action',
                'id': 'mid_node',
                'state': 'valid',
                'comment': '',
                'actors': {
                    '_type': ':map',
                    'items': {
                        'manager': {
                            '_type': 'actor',
                            'state': 'valid',
                            'user': {
                                '_type': 'user',
                                'identifier': 'manager',
                                'fullname': None,
                            },
                            'forms': [Form.state_json('mid_form', [
                                {
                                    '_type': 'field',
                                    'state': 'valid',
                                    'value': 'yes',
                                    'value_caption': 'yes',
                                    'name': 'data',
                                },
                            ])],
                        },
                    },
                },
                'milestone': False,
                'name': 'Segundo paso',
                'description': 'añadir información',
            },

            'final_node': {
                '_type': 'node',
                'type': 'action',
                'id': 'final_node',
                'state': 'ongoing',
                'comment': '',
                'actors': {
                    '_type': ':map',
                    'items': {},
                },
                'milestone': False,
                'name': '',
                'description': '',
            },
        },
        'item_order': [
            'start_node',
            'mid_node',
            'final_node',
        ],
    }

    assert reg['values'] == {
        '_execution': [{
            'name': '',
            'description': '',
        }],
        'mid_form': [{
            'data': 'yes',
        }],
    }

    assert reg['actors'] == {
        'start_node': 'juan',
        'mid_node': 'manager',
    }

    assert manager in execution.proxy.actors
    assert execution in manager.proxy.activities
Ejemplo n.º 7
0
def make_user(identifier, name, email=None):
    u = User(identifier=identifier, fullname=name, email=email).save()
    token = Token(token=random_string(9)).save()
    token.user.set(u)

    return u
Ejemplo n.º 8
0
    def patch(self, message):
        execution = Execution.get_or_exception(message['execution_id'])
        if execution.status != 'ongoing':
            raise ModelNotFoundError(
                'Specified execution never existed, and never will', )

        xml = Xml.load(self.config, execution.process_name, direct=True)

        # set nodes with pointers as unfilled, delete pointers
        updates = {}

        user = User.get_by(
            'identifier',
            message.get('user_identifier'),
        )

        if user is None:
            if message.get('user_identifier') == '__system__':
                user = User(identifier='__system__', fullname='System').save()
            else:
                raise InconsistentState('sent identifier of unexisten user')

        for pointer in execution.pointers.q().filter(status='ongoing'):
            updates['state.items.{node}.state'.format(
                node=pointer.node_id, )] = 'unfilled'
            pointer.status = 'cancelled'
            pointer.finished_at = datetime.now()
            pointer.save()

            self.pointer_collection().update_one({
                'id': pointer.id,
            }, {
                '$set': {
                    'state': 'cancelled',
                    'finished_at': pointer.finished_at,
                    'patch': {
                        'comment':
                        message['comment'],
                        'inputs':
                        message['inputs'],
                        'actor':
                        user.to_json(include=[
                            '_type',
                            'fullname',
                            'identifier',
                        ]),
                    },
                },
            })

        self.execution_collection().update_one({
            'id': execution.id,
        }, {
            '$set': updates,
        })

        # retrieve updated state
        state = next(self.execution_collection().find({'id': execution.id}))

        state_updates, array_filters = cascade_invalidate(
            xml, state, message['inputs'], message['comment'])

        # update state
        self.execution_collection().update_one(
            {'id': state['id']},
            {'$set': state_updates},
            array_filters=array_filters,
        )

        # retrieve updated state
        state = next(self.execution_collection().find({'id': execution.id}))

        first_invalid_node = track_next_node(xml, state, self.get_mongo(),
                                             self.config)

        # wakeup and start execution from the found invalid node
        self.wakeup_and_notify(first_invalid_node, execution, state)
Ejemplo n.º 9
0
def test_teardown(config, mongo):
    ''' second and last stage of a node's lifecycle '''
    # test setup
    handler = Handler(config)

    p_0 = make_pointer('simple.2018-02-19.xml', 'mid_node')
    execution = p_0.execution.get()
    execution.started_at = datetime(2018, 4, 1, 21, 45)
    execution.save()

    User(identifier='juan').save()
    manager = User(identifier='manager').save()
    manager2 = User(identifier='manager2').save()

    assert manager not in execution.actors.all()
    assert execution not in manager.activities.all()

    manager.tasks.set([p_0])
    manager2.tasks.set([p_0])

    state = Xml.load(config, execution.process_name).get_state()
    state['items']['start_node']['state'] = 'valid'

    mongo[config["EXECUTION_COLLECTION"]].insert_one({
        '_type':
        'execution',
        'id':
        execution.id,
        'state':
        state,
        'values': [
            {
                '_type':
                'fgroup',
                'ref':
                '_execution',
                'forms': [{
                    'ref':
                    '_execution',
                    'fields': [
                        {
                            '_type': 'field',
                            'name': 'name',
                            'value': '',
                            'value_caption': '',
                            'state': 'valid',
                            'actor': {
                                'identifier': '__system__',
                            },
                            'set_at': execution.started_at,
                        },
                        {
                            '_type': 'field',
                            'name': 'description',
                            'value': '',
                            'value_caption': '',
                            'state': 'valid',
                            'actor': {
                                'identifier': '__system__',
                            },
                            'set_at': execution.started_at,
                        },
                    ],
                }],
            },
        ],
        'actors': {
            'start_node': 'juan',
        },
    })

    mongo[config["POINTER_COLLECTION"]].insert_one({
        'id':
        p_0.id,
        'started_at':
        datetime(2018, 4, 1, 21, 45),
        'finished_at':
        None,
        'execution': {
            'id': execution.id,
        },
        'node': {
            'id': p_0.node_id,
        },
        'actors': {
            '_type': ':map',
            'items': {},
        },
        'actor_list': [],
    })

    # will teardown mid_node
    handler.step({
        'command':
        'step',
        'pointer_id':
        p_0.id,
        'user_identifier':
        manager.identifier,
        'input': [
            Form.state_json('mid_form', [
                {
                    '_type': 'field',
                    'state': 'valid',
                    'value': 'yes',
                    'value_caption': 'yes',
                    'name': 'data',
                },
            ])
        ],
    })

    # assertions
    assert Pointer.get(p_0.id).status == 'finished'
    ptrs = list(Pointer.q().filter(status='ongoing'))
    assert len(ptrs) == 1
    assert ptrs[0].node_id == 'final_node'

    # mongo has a registry
    reg = next(mongo[config["POINTER_COLLECTION"]].find())

    assert reg['started_at'] == datetime(2018, 4, 1, 21, 45)
    assert_near_date(reg['finished_at'])
    assert reg['execution']['id'] == execution.id
    assert reg['node']['id'] == p_0.node_id
    assert reg['actors'] == {
        '_type': ':map',
        'items': {
            'manager': {
                '_type':
                'actor',
                'state':
                'valid',
                'user': {
                    '_type': 'user',
                    'identifier': 'manager',
                    'fullname': None,
                    'email': None,
                },
                'forms': [
                    Form.state_json('mid_form', [
                        {
                            '_type': 'field',
                            'state': 'valid',
                            'value': 'yes',
                            'value_caption': 'yes',
                            'name': 'data',
                        },
                    ])
                ],
            },
        },
    }
    assert reg['actor_list'] == [
        {
            'form': 'mid_form',
            'actor': {
                '_type': 'user',
                'fullname': None,
                'identifier': 'manager',
                'email': None,
            },
        },
    ]

    # tasks where deleted from user
    assert list(manager.tasks.q().filter(status='ongoing')) == []
    assert list(manager2.tasks.q().filter(status='ongoing')) == []

    # state
    reg = next(mongo[config["EXECUTION_COLLECTION"]].find())

    assert reg['state'] == {
        '_type': ':sorted_map',
        'items': {
            'start_node': {
                '_type': 'node',
                'type': 'action',
                'id': 'start_node',
                'state': 'valid',
                'comment': '',
                'actors': {
                    '_type': ':map',
                    'items': {},
                },
                'milestone': False,
                'name': 'Primer paso',
                'description': 'Resolver una tarea',
            },
            'mid_node': {
                '_type': 'node',
                'type': 'action',
                'id': 'mid_node',
                'state': 'valid',
                'comment': '',
                'actors': {
                    '_type': ':map',
                    'items': {
                        'manager': {
                            '_type':
                            'actor',
                            'state':
                            'valid',
                            'user': {
                                '_type': 'user',
                                'identifier': 'manager',
                                'fullname': None,
                                'email': None,
                            },
                            'forms': [
                                Form.state_json('mid_form', [
                                    {
                                        '_type': 'field',
                                        'state': 'valid',
                                        'value': 'yes',
                                        'value_caption': 'yes',
                                        'name': 'data',
                                    },
                                ])
                            ],
                        },
                    },
                },
                'milestone': False,
                'name': 'Segundo paso',
                'description': 'añadir información',
            },
            'final_node': {
                '_type': 'node',
                'type': 'action',
                'id': 'final_node',
                'state': 'ongoing',
                'comment': '',
                'actors': {
                    '_type': ':map',
                    'items': {},
                },
                'milestone': False,
                'name': '',
                'description': '',
            },
        },
        'item_order': [
            'start_node',
            'mid_node',
            'final_node',
        ],
    }

    values = reg['values']

    eval_context = make_context({'values': values}, {})
    eval_actor_map = make_actor_map({'values': values})

    expected_context = {
        '_env': [{}],
        '_execution': [{
            'name': '',
            'description': '',
            'get_name_display': '',
            'get_description_display': '',
        }],
        'mid_form': [{
            'data': 'yes',
            'get_data_display': 'yes'
        }],
    }

    assert {k: list(v.all())
            for k, v in eval_context.items()} == expected_context

    expected_actor_map = {
        '_execution': [{
            'name': {
                'actor': '__system__',
            },
            'description': {
                'actor': '__system__',
            },
        }],
        'mid_form': [{
            'data': {
                'actor': 'manager',
            },
        }],
    }

    for frms in eval_actor_map.values():
        for frm in frms:
            for fld in frm.values():
                assert fld.pop('set_at')

    assert eval_actor_map == expected_actor_map

    assert reg['actors'] == {
        'start_node': 'juan',
        'mid_node': 'manager',
    }

    assert manager in execution.actors
    assert execution in manager.activities