def get_state(self): from cacahuate.node import make_node # noqa xmliter = iter(self) items = [] for node in xmliter: built_node = make_node(node, xmliter) items.append(built_node.get_state()) return SortedMap(items, key='id').to_json()
def test_request_node(config, mocker): class ResponseMock: status_code = 200 text = 'request response' mock = MagicMock(return_value=ResponseMock()) mocker.patch('requests.request', new=mock) xml = Xml.load(config, 'request.2018-05-18') xmliter = iter(xml) next(xmliter) request = next(xmliter) node = make_node(request, xmliter) response = node.make_request({ 'request': { 'data': '123456', }, }) requests.request.assert_called_once() args = requests.request.call_args method, url = args[0] data = args[1]['data'] headers = args[1]['headers'] assert method == 'GET' assert url == 'http://localhost/mirror?data=123456' assert headers == { 'content-type': 'application/json', 'x-url-data': '123456', } assert data == '{"data":"123456"}' assert response == { 'status_code': 200, 'response': 'request response', }
def call(self, message: dict, channel): pointer, user, input = self.recover_step(message) execution = pointer.proxy.execution.get() xml = Xml.load(self.config, execution.process_name, direct=True) xmliter = iter(xml) node = make_node(xmliter.find( lambda e: e.getAttribute('id') == pointer.node_id ), xmliter) # node's lifetime ends here self.teardown(node, pointer, user, input) # compute the next node in the sequence try: next_node, state = self.next(xml, node, execution) except EndOfProcess: # finish the execution return self.finish_execution(execution) self.wakeup_and_notify(next_node, execution, channel, state)
def test_resolve_params(config): xml = Xml.load(config, 'exit_request') xmliter = iter(xml) next(xmliter) node = make_node(next(xmliter), xmliter) state = { 'values': { 'exit_form': [{ 'reason': 'nones', }], }, 'actors': { 'requester': 'juan', }, } assert node.resolve_params(state) == { "identifier": 'juan', "relation": 'manager', "reason": 'nones', }
def cascade_invalidate(xml, state, invalidated, comment): ''' computes a set of fields to be marked as invalid given the original `invalidated` set of fields. ''' # because this could cause a recursive import from cacahuate.node import make_node # find the first node that is invalid and select it set_values = { i['ref']: { 'value': i['value'], 'value_caption': i['value_caption'], } for i in invalidated if 'value' in i } invalid_refs = set(i['ref'] for i in invalidated) xmliter = iter(xml) for element in xmliter: node = make_node(element, xmliter) more_fields = node.get_invalidated_fields(invalid_refs, state) invalid_refs.update(more_fields) # computes the keys and values to be used in a mongodb update to set the # fields as invalid updates = dict() for key in invalid_refs: node, actor, form, input = key.split('.') index, ref = form.split(':') ref_index = get_ref_index( state=state, node=node, actor=actor, ref=ref, index=index, ) node_path = 'state.items.{node}'.format(node=node) comment_path = node_path + '.comment' node_state_path = node_path + '.state' actor_path = node_path + '.actors.items.{actor}'.format(actor=actor) actor_state_path = actor_path + '.state' form_path = actor_path + '.forms.{index}'.format(index=index) form_state_path = form_path + '.state' input_path = form_path + '.inputs.items.{input}'.format(input=input) input_state_path = input_path + '.state' input_value_path = input_path + '.value' input_caption_path = input_path + '.value_caption' values_input_path = 'values.{ref}.{ref_index}.{input}'.format( ref=ref, ref_index=ref_index, input=input, ) # inputs input_state = 'valid' if key in set_values else 'invalid' updates[input_state_path] = input_state if key in set_values: updates[input_value_path] = set_values[key]['value'] updates[input_caption_path] = set_values[key]['value_caption'] if ref_index is not None: updates[values_input_path] = set_values[key]['value'] # forms if input_state == 'valid' and (form_state_path not in updates or updates[form_state_path] == 'valid'): form_state = 'valid' else: form_state = 'invalid' updates[form_state_path] = form_state # actors if form_state == 'valid' and (actor_state_path not in updates or updates[actor_state_path] == 'valid'): actor_state = 'valid' else: actor_state = 'invalid' updates[actor_state_path] = actor_state # nodes if actor_state == 'valid' and (node_state_path not in updates or updates[node_state_path] == 'valid'): node_state = 'valid' else: node_state = 'invalid' updates[node_state_path] = node_state updates[comment_path] = comment return updates
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 test_variable_proc_name_mix(config, mongo): ''' Test where the name is related to multiple forms in diferent nodes of the execution''' handler = Handler(config) user = make_user('juan', 'Juan') channel = MagicMock() xml = Xml.load(config, 'variable_name_mix.2020-01-28.xml') xmliter = iter(xml) node = make_node(next(xmliter), xmliter) input = [ Form.state_json('form01', [ { 'name': 'data01', 'type': 'text', 'value': '1', 'value_caption': '1', }, ]) ] execution = xml.start(node, input, mongo, channel, user.identifier) ptr = execution.proxy.pointers.get()[0] handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'node02' execution.reload() assert execution.name == 'Variable name process in step 10' assert execution.description == 'Description is also variable: 1, , ' handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', }, ]) ], }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'node03' execution.reload() assert execution.name == 'Variable name process in step 210' assert execution.description == 'Description is also variable: 1, 2, ' handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', }, ]) ], }, channel)
def test_variable_proc_name_pointers(config, mongo): ''' Test pointer name's update''' handler = Handler(config) user = make_user('juan', 'Juan') channel = MagicMock() xml = Xml.load(config, 'variable_name_mix.2020-01-28.xml') xmliter = iter(xml) node = make_node(next(xmliter), xmliter) input = [ Form.state_json('form01', [ { 'name': 'data01', 'type': 'text', 'value': '1', 'value_caption': '1', }, ]) ] execution = xml.start(node, input, mongo, channel, user.identifier) ptr = execution.proxy.pointers.get()[0] handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'node02' execution.reload() assert execution.name == 'Variable name process in step 10' assert execution.description == 'Description is also variable: 1, , ' handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', }, ]) ], }, channel) # pointer moved assert Pointer.get(ptr.id) is None ptr = Pointer.get_all()[0] assert ptr.node_id == 'node03' execution.reload() assert execution.name == 'Variable name process in step 210' assert execution.description == 'Description is also variable: 1, 2, ' handler.call( { 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', }, ]) ], }, channel) # now check pointers last state cursor = mongo[config["POINTER_COLLECTION"]].find({ 'execution.id': execution.id, }) assert cursor.count() == 3 expected_name = 'Variable name process in step 3210' expected_desc = 'Description is also variable: 1, 2, 3' for item in cursor: assert item['execution']['name'] == expected_name assert item['execution']['description'] == expected_desc
def test_variable_proc_name_mix(config, mongo): ''' Test where the name is related to multiple forms in diferent nodes of the execution''' handler = Handler(config) user = make_user('juan', 'Juan') xml = Xml.load(config, 'variable_name_mix.2020-01-28.xml') xmliter = iter(xml) node = make_node(next(xmliter), xmliter) input = [ Form.state_json('form01', [ { 'name': 'data01', 'type': 'text', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ]) ] execution = xml.start(node, input, mongo, user.identifier) ptr = next(execution.pointers.q().filter(status='ongoing')) handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node02' execution.reload() assert execution.name == 'Variable name process in step 10' assert execution.description == 'Description is also variable: 1, , ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ]) ], }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node03' execution.reload() assert execution.name == 'Variable name process in step 210' assert execution.description == 'Description is also variable: 1, 2, ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', 'state': 'valid', }, ]) ], })
def test_variable_proc_name_pointers(config, mongo): ''' Test pointer name's update''' handler = Handler(config) user = make_user('juan', 'Juan') xml = Xml.load(config, 'variable_name_mix.2020-01-28.xml') xmliter = iter(xml) node = make_node(next(xmliter), xmliter) input = [ Form.state_json('form01', [ { 'name': 'data01', 'type': 'text', 'value': '1', 'value_caption': '1', 'state': 'valid', }, ]) ] execution = xml.start(node, input, mongo, user.identifier) ptr = next(execution.pointers.q().filter(status='ongoing')) handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': input, }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node02' execution.reload() assert execution.name == 'Variable name process in step 10' assert execution.description == 'Description is also variable: 1, , ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form02', [ { 'name': 'data02', 'type': 'text', 'value': '2', 'value_caption': '2', 'state': 'valid', }, ]) ], }) # pointer moved assert Pointer.get(ptr.id).status == 'finished' ptr = next(Pointer.q().filter(status='ongoing')) assert ptr.node_id == 'node03' execution.reload() assert execution.name == 'Variable name process in step 210' assert execution.description == 'Description is also variable: 1, 2, ' handler.step({ 'command': 'step', 'pointer_id': ptr.id, 'user_identifier': user.identifier, 'input': [ Form.state_json('form03', [ { 'name': 'data03', 'type': 'text', 'value': '3', 'value_caption': '3', 'state': 'valid', }, ]) ], }) # now check pointers last state query = {'execution.id': execution.id} assert mongo[config["POINTER_COLLECTION"]].count_documents(query) == 3 expected_name = 'Variable name process in step 3210' expected_desc = 'Description is also variable: 1, 2, 3' cursor = mongo[config["POINTER_COLLECTION"]].find(query) for item in cursor: assert item['execution']['name'] == expected_name assert item['execution']['description'] == expected_desc
def cascade_invalidate(xml, state, invalidated, comment): ''' computes a set of fields to be marked as invalid given the original `invalidated` set of fields. ''' # because this could cause a recursive import from cacahuate.node import make_node # find the first node that is invalid and select it set_values = { i['ref']: { 'value': i['value'], 'value_caption': i['value_caption'], } for i in invalidated if 'value' in i } invalid_refs = set(i['ref'] for i in invalidated) xmliter = iter(xml) for element in xmliter: node = make_node(element, xmliter) more_fields = node.get_invalidated_fields(invalid_refs, state) invalid_refs.update(more_fields) # computes the keys and values to be used in a mongodb update to set the # fields as invalid updates = {} form_refs = [] field_names = [] array_filters = {} for key in invalid_refs: node, actor, form_parts, input_name = key.split('.') index, ref = form_parts.split(':') if ref not in form_refs: form_refs.append(ref) fg_index = form_refs.index(ref) if input_name not in field_names: field_names.append(input_name) fld_index = field_names.index(input_name) ref_index = get_ref_index( state=state, node=node, actor=actor, ref=ref, index=index, ) array_filters[f'frmRef{fg_index}.ref'] = { f'frmRef{fg_index}.ref': ref, } array_filters[f'fldName{fld_index}.name'] = { f'fldName{fld_index}.name': input_name, } node_path = 'state.items.{node}'.format(node=node) comment_path = node_path + '.comment' node_state_path = node_path + '.state' actor_path = node_path + '.actors.items.{actor}'.format(actor=actor) actor_state_path = actor_path + '.state' form_path = actor_path + '.forms.{index}'.format(index=index) form_state_path = form_path + '.state' input_path = form_path + '.inputs.items.{input_name}'.format( input_name=input_name) input_state_path = input_path + '.state' input_value_path = input_path + '.value' input_caption_path = input_path + '.value_caption' values_form_path = 'values.$[frmRef{fg_index}].forms.{ref_index}'.format( fg_index=fg_index, ref_index=ref_index, ) values_input_path = values_form_path + '.fields.$[fldName{fld_index}]'.format( fld_index=fld_index, ) values_input_value_path = values_input_path + '.value' values_input_value_caption_path = values_input_path + '.value_caption' values_input_state_path = values_input_path + '.state' # inputs input_state = 'valid' if key in set_values else 'invalid' updates[input_state_path] = input_state updates[values_input_state_path] = input_state if key in set_values: updates[input_value_path] = set_values[key]['value'] updates[input_caption_path] = set_values[key]['value_caption'] if ref_index is not None: updates[values_input_value_path] = set_values[key]['value'] updates[values_input_value_caption_path] = set_values[key][ 'value_caption'] # forms if input_state == 'valid' and (form_state_path not in updates or updates[form_state_path] == 'valid'): form_state = 'valid' else: form_state = 'invalid' updates[form_state_path] = form_state # actors if form_state == 'valid' and (actor_state_path not in updates or updates[actor_state_path] == 'valid'): actor_state = 'valid' else: actor_state = 'invalid' updates[actor_state_path] = actor_state # nodes if actor_state == 'valid' and (node_state_path not in updates or updates[node_state_path] == 'valid'): node_state = 'valid' else: node_state = 'invalid' updates[node_state_path] = node_state updates[comment_path] = comment return updates, list(array_filters.values())