def deploy_multiple_files(self, *args): """ # Due to https://jira.camunda.com/browse/CAM-13105 we cannot use generic camunda client when dealing with # multiple files. We have to use plain REST then. """ fields = { 'deployment-name': f'{os.path.basename(args[0])}', } for file in args: filename = os.path.basename(file) fields[f'{filename}'] = (filename, open(file, 'rb'), 'application/octet-stream') multipart_data = MultipartEncoder(fields=fields) logger.debug(multipart_data.fields) response = requests.post( f'{self._shared_resources.camunda_url}/deployment/create', data=multipart_data, headers={'Content-Type': multipart_data.content_type}) json = response.json() try: response.raise_for_status() logger.debug(json) except HTTPError as e: logger.error(json) raise e return json
def download_file_from_variable(self, variable_name: str) -> str: """ For performance reasons, files are not retrieved automatically during `fetch workload`. If your task requires a file that is attached to a process instance, you need to download the file explicitly. Example: | ${variables} | *fetch workload* | _first_task_in_demo_ | | | *Dictionary Should Contain Key* | _${variables}_ | _my_file_ | | ${file} | *Download File From Variable* | ${variables}[my_file] | | """ if not self.FETCH_RESPONSE: logger.warn( 'Could not download file for variable. Maybe you did not fetch and lock a workitem before?' ) else: with self._shared_resources.api_client as api_client: api_instance = openapi_client.ProcessInstanceApi(api_client) try: response = api_instance.get_process_instance_variable_binary( id=self.FETCH_RESPONSE.process_instance_id, var_name=variable_name) logger.debug(response) except ApiException as e: raise ApiException( f"Exception when calling ExternalTaskApi->get_process_instance_variable_binary: {e}\n" ) return response
def throw_bpmn_error(self, error_code: str, error_message: str = None, variables: Dict[str, Any] = None, files: Dict = None): if not self.FETCH_RESPONSE: logger.warn( 'No task to complete. Maybe you did not fetch and lock a workitem before?' ) else: with self._shared_resources.api_client as api_client: api_instance = openapi_client.ExternalTaskApi(api_client) variables = CamundaResources.convert_dict_to_openapi_variables( variables) openapi_files = CamundaResources.convert_file_dict_to_openapi_variables( files) variables.update(openapi_files) bpmn_error = openapi_client.ExternalTaskBpmnError( worker_id=self.WORKER_ID, error_message=error_message, error_code=error_code, variables=variables) try: logger.debug(f"Sending BPMN error for task:\n{bpmn_error}") api_instance.handle_external_task_bpmn_error( self.FETCH_RESPONSE.id, external_task_bpmn_error=bpmn_error) self.drop_fetch_response() except ApiException as e: raise ApiException( f"Exception when calling ExternalTaskApi->handle_external_task_bpmn_error: {e}\n" )
def complete_task(self, result_set: Dict[str, Any] = None, files: Dict = None): """ Completes the task that was fetched before with `fetch workload`. *Requires `fetch workload` to run before this one, logs warning instead.* Additional variables can be provided as dictionary in _result_set_ . Files can be provided as dictionary of filename and patch. Examples: | _# fetch and immediately complete_ | | | *fetch workload* | _my_topic_ | | | *complete task* | | | | | _# fetch and complete with return values_ | | | *fetch workload* | _decide_on_dish_ | | ${new_variables} | *Create Dictionary* | _my_dish=salad_ | | | *complete task* | _result_set=${new_variables}_ | | | | _# fetch and complete with return values and files_ | | | *fetch workload* | _decide_on_haircut_ | | ${return_values} | *Create Dictionary* | _style=short hair_ | | ${files} | *Create Dictionary* | _should_look_like=~/favorites/beckham.jpg_ | | | *complete task* | _${return_values}_ | _${files}_ | """ if not self.FETCH_RESPONSE: logger.warn( 'No task to complete. Maybe you did not fetch and lock a workitem before?' ) else: with self._shared_resources.api_client as api_client: api_instance = openapi_client.ExternalTaskApi(api_client) variables = CamundaResources.convert_dict_to_openapi_variables( result_set) openapi_files = CamundaResources.convert_file_dict_to_openapi_variables( files) variables.update(openapi_files) complete_task_dto = openapi_client.CompleteExternalTaskDto( worker_id=self.WORKER_ID, variables=variables) try: logger.debug( f"Sending to Camunda for completing Task:\n{complete_task_dto}" ) api_instance.complete_external_task_resource( self.FETCH_RESPONSE.id, complete_external_task_dto=complete_task_dto) self.drop_fetch_response() except ApiException as e: raise ApiException( f"Exception when calling ExternalTaskApi->complete_external_task_resource: {e}\n" )
def deliver_message(self, message_name, **kwargs): """ Delivers a message using Camunda REST API: https://docs.camunda.org/manual/latest/reference/rest/message/post-message/ Example: | ${result} | deliver message | msg_payment_received | | ${result} | deliver message | msg_payment_received | process_variables = ${variable_dictionary} | | ${result} | deliver message | msg_payment_received | business_key = ${correlating_business_key} | """ with self._shared_resources.api_client as api_client: correlation_message: CorrelationMessageDto = CorrelationMessageDto( **kwargs) correlation_message.message_name = message_name if not 'result_enabled' in kwargs: correlation_message.result_enabled = True if 'process_variables' in kwargs: correlation_message.process_variables = CamundaResources.dict_to_camunda_json( kwargs['process_variables']) serialized_message = api_client.sanitize_for_serialization( correlation_message) logger.debug(f'Message:\n{serialized_message}') headers = self._shared_resources.api_client.default_headers.copy() headers['Content-Type'] = 'application/json' try: response = requests.post( f'{self._shared_resources.camunda_url}/message', json=serialized_message, headers=headers) except ApiException as e: raise ApiException(f'Failed to deliver message:\n{e}') try: response.raise_for_status() except HTTPError as e: logger.error(e) raise ApiException(response.text) if correlation_message.result_enabled: json = response.json() logger.debug(json) return json else: return {}
def download_file_from_variable(self, variable_name: str) -> str: if not self.FETCH_RESPONSE: logger.warn( 'Could not download file for variable. Maybe you did not fetch and lock a workitem before?' ) else: with self._shared_resources.api_client as api_client: api_instance = openapi_client.ProcessInstanceApi(api_client) try: response = api_instance.get_process_instance_variable_binary( id=self.FETCH_RESPONSE.process_instance_id, var_name=variable_name) logger.debug(response) except ApiException as e: logger.error( f"Exception when calling ExternalTaskApi->get_process_instance_variable_binary: {e}\n" ) return response
def set_camunda_configuration(self, configuration: dict): if 'host' not in configuration.keys(): raise ValueError( f"Incomplete configuration. Configuration must include at least the Camunda host url:\t{configuration}" ) # weird things happen when dictionary is not copied and keyword is called repeatedly. Somehow robot or python remember the configuration from the previous call camunda_config = configuration.copy() host = configuration['host'] camunda_config['host'] = url_normalize(f'{host}/engine-rest') if 'api_key' in configuration.keys(): api_key = configuration['api_key'] camunda_config['api_key'] = {'default': api_key} if 'api_key_prefix' in configuration.keys(): api_key_prefix = configuration['api_key_prefix'] camunda_config['api_key_prefix'] = {'default': api_key_prefix} logger.debug( f"New configuration for Camunda client:\t{camunda_config}") self._shared_resources.client_configuration = Configuration( **camunda_config)
def fetch_workload(self, topic: str, async_response_timeout=None, use_priority=None, **kwargs) -> Dict: """ Locks and fetches workloads from camunda on a given topic. Returns a list of variable dictionary. Each dictionary representing 1 workload from a process instance. If a process instance was fetched, the process instance is cached and can be retrieved by keyword `Get recent process instance` The only mandatory parameter for this keyword is *topic* which is the name of the topic to fetch workload from. More parameters can be added from the Camunda documentation: https://docs.camunda.org/manual/7.14/reference/rest/external-task/fetch/ If not provided, this keyword will use a lock_duration of 60000 ms (10 minutes) and set {{deserialize_value=True}} Examples: | ${input_variables} | *Create Dictionary* | _name=Robot_ | | | *start process* | _my_demo_ | _${input_variables}_ | | ${variables} | *fetch and lock workloads* | _first_task_in_demo_ | | | *Dictionary Should Contain Key* | _${variables}_ | _name_ | | | *Should Be Equal As String* | _Robot_ | _${variables}[name]_ | Example deserializing only some variables: | ${input_variables} | *Create Dictionary* | _name=Robot_ | _profession=Framework_ | | | *start process* | _my_demo_ | _${input_variables}_ | | ${variables_of_interest} | *Create List* | _profession_ | | ${variables} | *Fetch Workload* | _first_task_in_demo_ | _variables=${variables_of_interest}_ | | | *Dictionary Should Not Contain Key* | _${variables}_ | _name_ | | | *Dictionary Should Contain Key* | _${variables}_ | _profession_ | | | *Should Be Equal As String* | _Framework_ | _${variables}[profession]_ | """ api_response = [] with self._shared_resources.api_client as api_client: # Create an instance of the API class api_instance = openapi_client.ExternalTaskApi(api_client) if 'lock_duration' not in kwargs: kwargs['lock_duration'] = 60000 if 'deserialize_values' not in kwargs: kwargs['deserialize_values'] = True topic_dto = FetchExternalTaskTopicDto(topic_name=topic, **kwargs) fetch_external_tasks_dto = FetchExternalTasksDto( worker_id=self.WORKER_ID, max_tasks=1, async_response_timeout=async_response_timeout, use_priority=use_priority, topics=[topic_dto]) try: api_response = api_instance.fetch_and_lock( fetch_external_tasks_dto=fetch_external_tasks_dto) logger.info(api_response) except ApiException as e: logger.error( "Exception when calling ExternalTaskApi->fetch_and_lock: %s\n" % e) work_items: List[LockedExternalTaskDto] = api_response if work_items: logger.debug( f'Received {len(work_items)} work_items from camunda engine for topic:\t{topic}' ) else: logger.debug( f'Received no work items from camunda engine for topic:\t{topic}' ) if not work_items: return {} self.FETCH_RESPONSE = work_items[0] variables: Dict[str, VariableValueDto] = self.FETCH_RESPONSE.variables return CamundaResources.convert_openapi_variables_to_dict(variables)