def start_process(): validate_json(request.json, ['process_name']) try: xml = Xml.load(app.config, request.json['process_name']) except ProcessNotFound: raise NotFound([{ 'detail': '{} process does not exist'.format(request.json['process_name']), 'where': 'request.body.process_name', }]) except MalformedProcess as e: raise UnprocessableEntity([{ 'detail': str(e), 'where': 'request.body.process_name', }]) xmliter = iter(xml) node = make_node(next(xmliter), xmliter) # Check for authorization validate_auth(node, g.user) # check if there are any forms present input = node.validate_input(request.json) # get rabbit channel for process queue channel = get_channel() execution = xml.start(node, input, mongo.db, channel, g.user.identifier) return { 'data': execution.to_json(), }, 201
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
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
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