コード例 #1
0
    def patch(self, message, channel):
        execution = Execution.get_or_exception(message['execution_id'])
        xml = Xml.load(self.config, execution.process_name, direct=True)
        mongo = self.get_mongo()
        execution_collection = mongo[self.config['EXECUTION_COLLECTION']]
        pointer_collection = mongo[self.config['POINTER_COLLECTION']]

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

        for pointer in execution.proxy.pointers.q():
            updates['state.items.{node}.state'.format(
                node=pointer.node_id, )] = 'unfilled'
            pointer.delete()
            pointer_collection.update_one({
                'id': pointer.id,
            }, {
                '$set': {
                    'state': 'cancelled',
                    'finished_at': datetime.now(),
                    'patch': {
                        'comment': message['comment'],
                        'inputs': message['inputs'],
                    },
                },
            })

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

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

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

        # update state
        collection = mongo[self.config['EXECUTION_COLLECTION']]
        collection.update_one({
            'id': state['id'],
        }, {
            '$set': state_updates,
        })

        # retrieve updated state
        state = next(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, channel, state)
コード例 #2
0
def execution_add_user(id):
    ''' adds the user as a candidate for solving the given node, only if the
    node has an active pointer. '''
    # TODO possible race condition introduced here. How does this code work in
    # case the handler is moving the pointer?

    # get execution
    execution = Execution.get_or_exception(id)

    # validate the members needed
    validate_json(request.json, ['identifier', 'node_id'])

    identifier = request.json['identifier']
    node_id = request.json['node_id']

    # get actual pointer
    try:
        pointer = next(execution.proxy.pointers.q().filter(node_id=node_id))
    except StopIteration:
        raise BadRequest([{
            'detail': f'{node_id} does not have a live pointer',
            'code': 'validation.no_live_pointer',
            'where': 'request.body.node_id',
        }])

    # get user
    user = User.get_by('identifier', identifier)
    if user is None:
        raise InvalidInputError('user_id', 'request.body.identifier')

    # update user
    user.proxy.tasks.add(pointer)

    # update pointer
    collection = mongo.db[app.config['POINTER_COLLECTION']]
    db_pointer = collection.find_one({'id': pointer.id})
    user_json = user.to_json()
    notified_users = db_pointer.get('notified_users', [])

    if user_json not in notified_users:
        notified_users.append(user.to_json())

    collection.update_one(
        {'id': pointer.id},
        {'$set': {
            'notified_users': notified_users
        }},
    )

    return jsonify(user_json), 200
コード例 #3
0
def delete_process(id):
    execution = Execution.get_or_exception(id)

    channel = get_channel()
    channel.basic_publish(
        exchange='',
        routing_key=app.config['RABBIT_QUEUE'],
        body=json.dumps({
            'command': 'cancel',
            'execution_id': execution.id,
        }),
        properties=pika.BasicProperties(delivery_mode=2, ),
    )

    return jsonify({
        'data': 'accepted',
    }), 202
コード例 #4
0
ファイル: handler.py プロジェクト: cnpoe/cacahuate
    def cancel_execution(self, message):
        execution = Execution.get_or_exception(message['execution_id'])

        for pointer in execution.proxy.pointers.get():
            pointer.delete()

        collection = self.get_mongo()[
            self.config['EXECUTION_COLLECTION']
        ]

        collection.update_one({
            'id': execution.id,
        }, {
            '$set': {
                'status': 'cancelled',
                'finished_at': datetime.now()
            }
        })

        execution.delete()
コード例 #5
0
    def cancel_execution(self, message):
        execution = Execution.get_or_exception(message['execution_id'])
        if execution.status != 'ongoing':
            raise ModelNotFoundError(
                'Specified execution never existed, and never will', )

        execution.status = 'cancelled'
        execution.finished_at = datetime.now()
        execution.save()

        for pointer in execution.pointers.q().filter(status='ongoing'):
            pointer.status = 'cancelled'
            pointer.finished_at = datetime.now()
            pointer.save()

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

        self.pointer_collection().update_many(
            {
                'execution.id': execution.id,
                'state': 'ongoing',
            }, {
                '$set': {
                    'state': 'cancelled',
                    'finished_at': execution.finished_at
                }
            })

        self.pointer_collection().update_many({
            'execution.id': execution.id,
        }, {'$set': {
            'execution': execution.to_json(),
        }})
コード例 #6
0
    def cancel_execution(self, message):
        execution = Execution.get_or_exception(message['execution_id'])
        execution.status = 'cancelled'
        execution.finished_at = datetime.now()

        for pointer in execution.proxy.pointers.get():
            pointer.delete()

        exe_collection = self.get_mongo()[self.config['EXECUTION_COLLECTION']]

        exe_collection.update_one({
            'id': execution.id,
        }, {
            '$set': {
                'status': execution.status,
                'finished_at': execution.finished_at
            }
        })

        ptr_collection = self.get_mongo()[self.config['POINTER_COLLECTION']]

        ptr_collection.update_many(
            {
                'execution.id': execution.id,
                'state': 'ongoing',
            }, {
                '$set': {
                    'state': 'cancelled',
                    'finished_at': execution.finished_at
                }
            })

        ptr_collection.update_many({
            'execution.id': execution.id,
        }, {'$set': {
            'execution': execution.to_json(),
        }})

        execution.delete()
コード例 #7
0
def continue_process():
    validate_json(request.json, ['execution_id', 'node_id'])

    execution_id = request.json['execution_id']
    node_id = request.json['node_id']

    try:
        execution = Execution.get_or_exception(execution_id)
    except ModelNotFoundError:
        raise BadRequest([{
            'detail': 'execution_id is not valid',
            'code': 'validation.invalid',
            'where': 'request.body.execution_id',
        }])

    xml = Xml.load(app.config, execution.process_name, direct=True)
    xmliter = iter(xml)

    try:
        continue_point = make_node(
            xmliter.find(lambda e: e.getAttribute('id') == node_id), xmliter)
    except ElementNotFound:
        raise BadRequest([{
            'detail': 'node_id is not a valid node',
            'code': 'validation.invalid_node',
            'where': 'request.body.node_id',
        }])

    try:
        pointer = next(execution.proxy.pointers.q().filter(node_id=node_id))
    except StopIteration:
        raise BadRequest([{
            'detail': 'node_id does not have a live pointer',
            'code': 'validation.no_live_pointer',
            'where': 'request.body.node_id',
        }])

    # Check for authorization
    if pointer not in g.user.proxy.tasks:
        raise Forbidden([{
            'detail': 'Provided user does not have this task assigned',
            'where': 'request.authorization',
        }])

    # Validate asociated forms
    collected_input = continue_point.validate_input(request.json)

    # trigger rabbit
    channel = get_channel()
    channel.basic_publish(
        exchange='',
        routing_key=app.config['RABBIT_QUEUE'],
        body=json.dumps({
            'command': 'step',
            'pointer_id': pointer.id,
            'user_identifier': g.user.identifier,
            'input': collected_input,
        }),
        properties=pika.BasicProperties(delivery_mode=2, ),
    )

    return {
        'data': 'accepted',
    }, 202
コード例 #8
0
def execution_patch(id):
    execution = Execution.get_or_exception(id)
    collection = mongo.db[app.config['EXECUTION_COLLECTION']]
    execution_state = next(collection.find({'id': id}))

    validate_json(request.json, ['comment', 'inputs'])

    xml = Xml.load(app.config, execution.process_name, direct=True)
    dom = xml.get_dom()

    if type(request.json['inputs']) != list:
        raise RequiredListError('inputs', 'request.body.inputs')

    processed_inputs = []

    for i, field in enumerate(request.json['inputs']):
        if type(field) != dict:
            raise RequiredDictError(str(i), 'request.body.inputs.{}'.format(i))

        if 'ref' not in field:
            raise RequiredInputError('id',
                                     'request.body.inputs.{}.ref'.format(i))

        if type(field['ref']) != str:
            raise RequiredStrError('ref',
                                   'request.body.inputs.{}.ref'.format(i))

        # check down the state tree for existence of the requested ref
        processed_ref = []
        pieces = field['ref'].split('.')

        try:
            node_id = pieces.pop(0)
            node_state = execution_state['state']['items'][node_id]
        except IndexError:
            raise InputError('Missing segment in ref for node_id',
                             'request.body.inputs.{}.ref'.format(i),
                             'validation.invalid')
        except KeyError:
            raise InputError('node {} not found'.format(node_id),
                             'request.body.inputs.{}.ref'.format(i),
                             'validation.invalid')

        if node_state['type'] != 'action':
            raise InputError('only action nodes may be patched',
                             'request.body.inputs.{}.ref'.format(i),
                             'validation.invalid')

        processed_ref.append(node_id)

        # node xml element
        node = get_element_by(dom, 'action', 'id', node_id)

        if len(node_state['actors']['items']) == 1:
            only_key = list(node_state['actors']['items'].keys())[0]
            actor_state = node_state['actors']['items'][only_key]
        else:
            try:
                actor_username = pieces.pop(0)
                actor_state = node_state['actors']['items'][actor_username]
            except IndexError:
                raise InputError('Missing segment in ref for actor username',
                                 'request.body.inputs.{}.ref'.format(i),
                                 'validation.invalid')
            except KeyError:
                raise InputError('actor {} not found'.format(actor_username),
                                 'request.body.inputs.{}.ref'.format(i),
                                 'validation.invalid')

        processed_ref.append(actor_state['user']['identifier'])

        try:
            form_ref = pieces.pop(0)
        except IndexError:
            raise InputError('Missing segment in ref for form ref',
                             'request.body.inputs.{}.ref'.format(i),
                             'validation.invalid')

        if re.match(r'\d+', form_ref):
            try:
                form_index = int(form_ref)
                form_state = actor_state['forms'][form_index]
            except KeyError:
                raise InputError('form index {} not found'.format(form_ref),
                                 'request.body.inputs.{}.ref'.format(i),
                                 'validation.invalid')
        else:
            matching_forms = list(
                map(lambda f: f['ref'] == form_ref, actor_state['forms']))
            form_count = len(list(filter(lambda x: x, matching_forms)))

            if form_count == 1:
                form_index = matching_forms.index(True)
                form_state = actor_state['forms'][form_index]
            elif form_count == 0:
                raise InputError(
                    'No forms with ref {} in node'.format(form_ref),
                    'request.body.inputs.{}.ref'.format(i),
                    'validation.invalid')
            else:
                raise InputError(
                    'More than one form with ref {}'.format(form_ref),
                    'request.body.inputs.{}.ref'.format(i),
                    'validation.invalid')

        processed_ref.append(str(form_index) + ':' + form_state['ref'])

        # form xml element
        form = get_element_by(node, 'form', 'id', form_state['ref'])

        try:
            input_name = pieces.pop(0)
            form_state['inputs']['items'][input_name]
        except IndexError:
            raise InputError('Missing segment in ref for input name',
                             'request.body.inputs.{}.ref'.format(i),
                             'validation.invalid')
        except KeyError:
            raise InputError('input {} not found'.format(input_name),
                             'request.body.inputs.{}.ref'.format(i),
                             'validation.invalid')

        processed_ref.append(input_name)

        processed_inputs.append({
            'ref': '.'.join(processed_ref),
        })

        # input xml element
        input_el = get_element_by(form, 'input', 'name', input_name)

        if 'value' in field:
            try:
                input_obj = make_input(input_el)
                value = input_obj.validate(field['value'], 0)
                caption = input_obj.make_caption(value)

                processed_inputs[-1]['value'] = value
                processed_inputs[-1]['value_caption'] = caption
            except InputError as e:
                raise InputError('value invalid: {}'.format(str(e)),
                                 'request.body.inputs.{}.value'.format(i),
                                 'validation.invalid')

    channel = get_channel()
    channel.basic_publish(
        exchange='',
        routing_key=app.config['RABBIT_QUEUE'],
        body=json.dumps({
            'command': 'patch',
            'execution_id': execution.id,
            'comment': request.json['comment'],
            'inputs': processed_inputs,
        }),
        properties=pika.BasicProperties(delivery_mode=2, ),
    )

    return jsonify({
        'data': 'accepted',
    }), 202
コード例 #9
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)