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 get_amount_of_workloads(self, topic: str, **kwargs) -> int:
        """
        Retrieves count of tasks. By default expects a topic name, but all parameters from the original endpoint
        may be provided: https://docs.camunda.org/manual/latest/reference/rest/external-task/get-query-count/
        """
        with self._shared_resources.api_client as api_client:
            api_instance = openapi_client.ExternalTaskApi(api_client)

            try:
                response: CountResultDto = api_instance.get_external_tasks_count(
                    topic_name=topic, **kwargs)
            except ApiException as e:
                raise ApiException(
                    f'Failed to count workload for topic "{topic}":\n{e}')

        logger.info(f'Amount of workloads for "{topic}":\t{response.count}')
        return response.count
 def unlock(self):
     """
     Unlocks recent task.
     """
     if not self.FETCH_RESPONSE:
         logger.warn(
             'No task to unlock. 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)
             try:
                 api_instance.unlock(self.FETCH_RESPONSE.id)
                 self.drop_fetch_response()
             except ApiException as e:
                 logger.error(
                     f"Exception when calling ExternalTaskApi->unlock: {e}\n"
                 )
    def notify_failure(self, **kwargs):
        """
        Raises a failure to Camunda. When retry counter is less than 1, an incident is created by Camunda.

        You can specify number of retries with the *retries* argument. If current fetched process instance already has
        *retries* parameter set, the *retries* argument of this keyword is ignored. Instead, the retries counter will
        be decreased by 1.

        CamundaLibrary takes care of providing the worker_id and task_id. *retry_timeout* is equal to *lock_duration* for external tasks.
        Check for camunda client documentation for all parameters of the request body: https://noordsestern.gitlab.io/camunda-client-for-python/7-15-0/docs/ExternalTaskApi.html#handle_failure

        Example:
        | *notify failure* |  |  |
        | *notify failure* | retries=3 | error_message=Task failed due to... |
        """
        current_process_instance = self.FETCH_RESPONSE
        if not current_process_instance:
            logger.warn(
                'No task to notify failure for. 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)
                if 'retry_timeout' not in kwargs or None is kwargs[
                        'retry_timeout'] or not kwargs['retry_timeout']:
                    kwargs['retry_timeout'] = self.DEFAULT_LOCK_DURATION

                if None is not current_process_instance.retries:
                    kwargs['retries'] = current_process_instance.retries - 1

                external_task_failure_dto = ExternalTaskFailureDto(
                    worker_id=self.WORKER_ID, **kwargs)

                try:
                    api_instance.handle_failure(
                        id=current_process_instance.id,
                        external_task_failure_dto=external_task_failure_dto)
                    self.drop_fetch_response()
                except ApiException as e:
                    raise ApiException(
                        "Exception when calling ExternalTaskApi->handle_failure: %s\n"
                        % e)
    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)