Ejemplo n.º 1
0
def get_min_distance_house_travel_distance(task: ExternalTask) -> TaskResult:
    """
    Gets the distance between the user's house and a travel company.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("get_min_distance_house_travel_distance")

    GEOGRAPHICAL_DISTACE_SERVICE = environ.get(
        "GEOGRAPHICAL_DISTACE_SERVICE", "http://geographical_distances:8080")

    travel_company = str(task.get_variable("travel_company"))
    travel_company_url = travel_company.split(';')[0]
    travel_company_address = travel_company.split(';')[1]
    offer_purchase_data = OfferPurchaseData.from_dict(
        json.loads(task.get_variable("offer_purchase_data")))

    distances = json.loads(str(task.get_variable("distances")))

    request = {
        "address_1": travel_company_address,
        "address_2": str(offer_purchase_data.address)
    }

    distance_request = requests.post(GEOGRAPHICAL_DISTACE_SERVICE +
                                     "/distance",
                                     json=request)

    distances.get("distances").append({
        "company": travel_company_url,
        "distance": distance_request.text
    })
    return task.complete(global_variables={"distances": json.dumps(distances)})
def send_wrong_offer_code(task: ExternalTask) -> TaskResult:
    """
    Notifies the user that the code is invalid, expired or already in use.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("send_wrong_offer_code")

    user_communication_code = str(task.get_variable("user_communication_code"))

    # Connects to RabbitMQ and publishes the message
    connection = pika.BlockingConnection(pika.ConnectionParameters("acmesky_mq"))
    channel = connection.channel()

    channel.queue_declare(queue=user_communication_code, durable=True)

    error = PurchaseProcessInformation(
        message="Il codice offerta inserito non è valido, è in uso da parte di un altro utente o sono passate più di 24 ore da quando è stato inviato.",
        communication_code=user_communication_code,
        is_error=True,
    )

    channel.basic_publish(
        exchange="",
        routing_key=user_communication_code,
        body=bytes(json.dumps(error.to_dict()), "utf-8"),
        properties=pika.BasicProperties(delivery_mode=2),
    )

    connection.close()

    return task.complete()
Ejemplo n.º 3
0
def rehabilitation_offer_code(task: ExternalTask) -> TaskResult:
    """
    Compensation task: if the verification offer code and payment sub process fail, this task rehabilitates the offer
    code for another try.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("rehabilitation_offer_code")

    offer_purchase_data = OfferPurchaseData.from_dict(
        json.loads(task.get_variable("offer_purchase_data")))

    offer_code = offer_purchase_data.offer_code

    # Connects to postgreSQL
    Session = sessionmaker(bind=create_sql_engine())
    session = Session()
    """
    Gets the offer match that has to be rehabilitated from the DB.
    The offer match is the one blocked and with offer_code equal to the process offer_code variable.
    """
    affected_rows = session.query(OfferMatch).filter(
        OfferMatch.offer_code == offer_code).update(
            {"blocked": False}, synchronize_session="fetch")
    if affected_rows < 1:
        session.rollback()
        logger.error(
            f"{affected_rows} matches were found for the given offer code. The offer code will not be rehabilitated."
        )
        return task.complete()

    logger.info(f"{affected_rows} match was found for the given offer code.")
    session.commit()
    return task.complete()
def notify_user_via_prontogram(task: ExternalTask) -> TaskResult:
    """
    For each offer code, sends a message to the related ProntoGram user.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("notify_user_via_prontogram")

    offer_codes = json.loads(task.get_variable("offer_codes"))
    offer_infos = json.loads(task.get_variable("offer_infos"))
    # Quotes are added at the beginning and end of the pg_username, [1:-1] removes them.
    prontogram_username = str(task.get_variable("prontogram_username"))[1:-1]
    logger.info(f"prontogram username: {prontogram_username}")
    logger.info(f"offer codes: {offer_codes}")

    for offer_code, offer_info in zip(offer_codes, offer_infos):
        prontogram_message = {
            "sender":
            "ACMESky",
            "receiver":
            prontogram_username,
            "body":
            f"ACMESky ha trovato per te la seguente offerta:\n{offer_info}\nInserisci il codice offerta {offer_code} sul sito di ACMESky per poterne usufruire. Affrettati, sarà valido per sole 24 ore!"
        }

        # logger.info(json.dumps(prontogram_message))
        r = requests.post("http://prontogram_backend:8080/messages",
                          json=prontogram_message)

        logger.info(f"ProntoGram response: {r.status_code}")
        if r.status_code >= 300:
            logger.warn(r.text)
    return task.complete()
def send_correct_offer_code(task: ExternalTask) -> TaskResult:
    """
    Sends the confirmation that the offer code inserted is valid to the user
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("send_correct_offer_code")

    user_communication_code = str(task.get_variable("user_communication_code"))

    # Connects to RabbitMQ and publishes the message
    connection = pika.BlockingConnection(pika.ConnectionParameters(host="acmesky_mq"))
    channel = connection.channel()
    channel.queue_declare(queue=user_communication_code, durable=True)
    success = PurchaseProcessInformation(message=f"Il codice offerta inserito è valido.",
                                         communication_code=user_communication_code)

    channel.basic_publish(
        exchange="",
        routing_key=user_communication_code,
        body=bytes(json.dumps(success.to_dict()), "utf-8"),
        properties=pika.BasicProperties(delivery_mode=2),
    )

    connection.close()

    return task.complete()
Ejemplo n.º 6
0
def fail_task_handler(task: ExternalTask):
    log_context = {"WORKER_ID": task.get_worker_id(),
                   "TASK_ID": task.get_task_id(),
                   "TOPIC": task.get_topic_name()}

    log_with_context("executing fail_task_handler", log_context)
    return task.failure("task failed", "task failed forced", 0, 10)
Ejemplo n.º 7
0
def register_interest(task: ExternalTask) -> TaskResult:
    """
    Saves the interest in MongoDB.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("register_interest")

    interest = json.loads(task.get_variable("interest"))
    # The user pushes the interest without the field "offer_codes" (which is necessary for later updates).
    interest["offer_codes"] = []
    """ Connection and save on MongoDB
    """
    username = environ.get("MONGO_USER", "root")
    password = environ.get("MONGO_PASSWORD", "password")
    client = MongoClient(f"mongodb://{username}:{password}@acmesky_mongo:27017"
                         )  # Connects to MongoDB
    acmesky_db = client['ACMESky']  # Selects the right DB
    interests_collection = acmesky_db[
        'interests']  # Selects the right document

    # Inserting into the DB only if it does not already exist.
    if not interests_collection.find_one(interest):
        interests_collection.insert_one(interest)

    return task.complete({"operation_result": "OK"})
Ejemplo n.º 8
0
def handle_task(task: ExternalTask) -> TaskResult:
    """
    This task handler you need to implement with your business logic.
    After completion of business logic call either task.complete() or task.failure() or task.bpmn_error() 
    to report status of task to Camunda
    """
    # add your business logic here
    # ...
    print('Service invoked')
    cars = task.get_variable("cars")
    car_dict = json.loads(cars)
    de = []
    da = []
    for key in car_dict:
        if car_dict.get(key).get('country') == 'DE':
            de.append(car_dict.get(key).get('price'))
        if car_dict.get(key).get('country') == 'DK':
            da.append(car_dict.get(key).get('price'))

    duty = (sum(da) / len(da)) - (sum(de) / len(de))
    # mark task either complete/failure/bpmnError based on outcome of your business logic

    # if failure:
    #     # this marks task as failed in Camunda
    #     return task.failure(error_message="task failed",  error_details="failed task details",
    #                         max_retries=3, retry_timeout=5000)
    # elif bpmn_error:
    #     return task.bpmn_error(error_code="BPMN_ERROR_CODE", error_message="BPMN Error occurred",
    #                             variables={"var1": "value1", "success": False})

    # pass any output variables you may want to send to Camunda as dictionary to complete()
    return task.complete({"duty": duty})
Ejemplo n.º 9
0
def get_flight_offers(task: ExternalTask) -> TaskResult:
    """
    Gets the flights from a Flight Company
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("get_flight_offers")

    url = task.get_variable("company")
    logger.info("Contacting: " + url)

    new_flights = requests.get(url + "/flights/offers").json()

    # Workaround for Camunda limitation on the length of the string that can be saved as process variable.
    if new_flights == {}:
        logger.info(f"Empty json from the flight company at URL {url}")
        return task.complete({'offers_0': dumps([]), 'offers_packets': 1})
    else:
        # Workaround: Camunda string global variables can hold maximum 4000 chars per string.
        # Therefore we must split the dumped string every 3500 characters (just to be sure).
        stringified_flights = dumps(new_flights.get('flights'))
        offers_packets = (len(stringified_flights) // 3500) + 1
        global_vars = {'offers_packets': offers_packets}
        for packet in range(offers_packets):
            start = packet * 3500
            end = start + 3500
            global_vars[f'offers_{packet}'] = stringified_flights[start:end]
        return task.complete(global_variables=global_vars)
    def test_task_with_retries_returns_failure_task_result_with_decremented_retries(self):
        retries = 3
        task = ExternalTask(context={"retries": retries})
        task_result = task.failure(error_message="unknown error", error_details="error details here",
                                   max_retries=10, retry_timeout=1000)

        self.assertEqual(retries - 1, task_result.retries)
Ejemplo n.º 11
0
def generic_task_handler(task: ExternalTask):
    log_context = {"WORKER_ID": task.get_worker_id(),
                   "TASK_ID": task.get_task_id(),
                   "TOPIC": task.get_topic_name()}

    log_with_context("executing generic task handler", log_context)
    return task.complete()
Ejemplo n.º 12
0
def send_wrong_payment_status(task: ExternalTask) -> TaskResult:
    """
    Notifies the user that the payment has timed out.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("send_wrong_payment_status")

    user_communication_code = str(task.get_variable("user_communication_code"))

    # Connect to RabbitMQ and publish the message
    connection = pika.BlockingConnection(
        pika.ConnectionParameters("acmesky_mq"))
    channel = connection.channel()

    channel.queue_declare(queue=user_communication_code, durable=True)

    error = PurchaseProcessInformation(
        message="Il processo di acquisto è fallito. Riprova nuovamente.",
        communication_code=user_communication_code,
        is_error=True,
    )

    channel.basic_publish(
        exchange="",
        routing_key=user_communication_code,
        body=bytes(json.dumps(error.to_dict()), "utf-8"),
        properties=pika.BasicProperties(delivery_mode=2),
    )

    connection.close()

    return task.complete()
def handle_task(task: ExternalTask):
    log_context = {
        "WORKER_ID": task.get_worker_id(),
        "TASK_ID": task.get_task_id(),
        "TOPIC": task.get_topic_name()
    }

    log_with_context(
        f"handle_task started: business key = {task.get_business_key()}",
        log_context)

    # simulate task execution
    execution_time = randint(0, 10)
    log_with_context(
        f"handle_task - business logic execution started for task: "
        f"it will execute for {execution_time} seconds", log_context)
    time.sleep(execution_time)

    # simulate that task results randomly into failure/BPMN error/complete
    failure = random_true()
    bpmn_error = False if failure else random_true()
    # override the values to simulate success/failure/BPMN error explicitly (if needed)
    failure, bpmn_error = False, False
    log_with_context(
        f"handle_task - business logic executed: failure: {failure}, bpmn_error: {bpmn_error}",
        log_context)

    return __handle_task_result(task, failure, bpmn_error)
 def test_get_property_returns_value_for_property_present(self):
     task = ExternalTask(
         context={"extensionProperties": {
             "var1": "one",
             "var2": "two"
         }})
     prop = task.get_extension_property("var1")
     self.assertEqual("one", prop)
    def test_complete_returns_success_task_result(self):
        task = ExternalTask(context={})
        task_result = task.complete({})

        self.assertEqual(task, task_result.get_task())
        self.assertEqual(task_result, task.get_task_result())

        self.assertTrue(task_result.is_success())
        self.assertFalse(task_result.is_failure())
        self.assertFalse(task_result.is_bpmn_error())
    def test_external_task_creation_from_context(self):
        context = {
            "id": "123",
            "workerId": "321",
            "topicName": "my_topic",
            "tenantId": "tenant1",
            "processInstanceId": "processInstanceId1",
            "variables": {
                "applicationId": {
                    "type": "String",
                    "value": "appId987",
                    "valueInfo": {}
                }
            }
        }
        task = ExternalTask(context=context)

        self.assertEqual("123", task.get_task_id())
        self.assertEqual("321", task.get_worker_id())
        self.assertEqual("my_topic", task.get_topic_name())
        self.assertEqual("tenant1", task.get_tenant_id())
        self.assertEqual("processInstanceId1", task.get_process_instance_id())
        self.assertDictEqual({"applicationId": "appId987"},
                             task.get_variables())
        self.assertEqual("empty_task_result", str(task.get_task_result()))
    def test_task_bpmn_error(self):
        task = ExternalTask({"id": "1", "topicName": "my_topic"})
        expected_task_result = TaskResult.bpmn_error(task, error_code="bpmn_err_code_1", error_message="bpmn error")

        external_task_client = ExternalTaskClient(worker_id=1)
        responses.add(responses.POST, external_task_client.get_task_bpmn_error_url(task.get_task_id()),
                      status=HTTPStatus.NO_CONTENT)
        executor = ExternalTaskExecutor(worker_id=1, external_task_client=external_task_client)

        actual_task_result = executor.execute_task(task, self.task_bpmn_error_action)
        self.assertEqual(str(expected_task_result), str(actual_task_result))
def verify_offer_code_validity(task: ExternalTask) -> TaskResult:
    """
    Verifies that the offer code is valid, not expired and not already in use by another user.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("verify_offer_code_validity")

    offer_purchase_data = OfferPurchaseData.from_dict(
        json.loads(task.get_variable("offer_purchase_data")))

    offer_code = offer_purchase_data.offer_code

    # Connects to PostgreSQL
    Session = sessionmaker(bind=create_sql_engine())
    session = Session()

    user_communication_code = str(hash(offer_purchase_data))

    # Checks if the offer matched is blocked
    matches = session.query(OfferMatch).filter(
        OfferMatch.offer_code == offer_code, OfferMatch.blocked == True).all()
    if len(matches) == 1:
        logger.error(f"Offer code is BLOCKED.")
        return task.complete(
            global_variables={
                'offer_code_validity': False,
                'user_communication_code': user_communication_code
            })

    # Checks if the offer match is not expired and sets it to blocked=True.
    affected_rows = session.query(OfferMatch).filter(
        OfferMatch.offer_code == offer_code, OfferMatch.creation_date >=
        datetime.datetime.now(tz=datetime.timezone.utc) -
        datetime.timedelta(hours=24)).update({"blocked": True},
                                             synchronize_session="fetch")
    if affected_rows < 1:
        session.rollback()
        logger.error(
            f"{affected_rows} matches were found for the given offer code.")
        return task.complete(
            global_variables={
                'offer_code_validity': False,
                'user_communication_code': user_communication_code
            })

    logger.info(f"{affected_rows} match was found for the given offer code.")
    session.commit()
    return task.complete(
        global_variables={
            'offer_code_validity': True,
            'user_communication_code': user_communication_code
        })
Ejemplo n.º 19
0
async def number_check(task: ExternalTask) -> ExternalTaskResult:
    try:
        number = task.context_variables["number"]
        print(f"We received {number} for checking...")
        task.local_variables.set_variable(
            "result", "true" if int(number) % 2 != 0 else "false", Variables.ValueType.STRING
        )
        return task.complete()
    except Exception as err:
        print(f"Oh no! Something went wrong: {err}")
        return task.failure()
async def test_task_fail(context, mocker: MockerFixture):
    error_message = "NotImplementedError"
    error_details = "This method has not been implemented"
    max_retries = 3
    retry_timeout = 5
    task = ExternalTask(context)
    res = task.failure(
        error_message=error_message,
        error_details=error_details,
        max_retries=max_retries,
        retry_timeout=retry_timeout,
    )
    def test_task_complete(self):
        task = ExternalTask({"id": "1", "topicName": "my_topic"})
        output_vars = {"var1": 1, "var2": "value", "var3": True}
        expected_task_result = TaskResult.success(task, output_vars)

        external_task_client = ExternalTaskClient(worker_id=1)
        responses.add(responses.POST, external_task_client.get_task_complete_url(task.get_task_id()),
                      status=HTTPStatus.NO_CONTENT)
        executor = ExternalTaskExecutor(worker_id=1, external_task_client=external_task_client)

        actual_task_result = executor.execute_task(task, self.task_success_action)
        self.assertEqual(str(expected_task_result), str(actual_task_result))
    def test_bpmn_error_returns_bpmn_error_task_result(self):
        task = ExternalTask(context={})
        task_result = task.bpmn_error(error_code="bpmn_error_code_1", error_message="bpmn error")

        self.assertEqual(task, task_result.get_task())
        self.assertEqual(task_result, task.get_task_result())

        self.assertFalse(task_result.is_success())
        self.assertFalse(task_result.is_failure())
        self.assertTrue(task_result.is_bpmn_error())

        self.assertEqual("bpmn_error_code_1", task_result.bpmn_error_code)
Ejemplo n.º 23
0
def book_transfer(task: ExternalTask) -> TaskResult:
    """
    Contacts the chosen Travel Company and requests to book a travel using the SOAP protocol
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("get_min_distance_house_travel_distance")

    distances = json.loads(str(task.get_variable("distances")))
    distances = distances.get("distances")
    offer_purchase_data = OfferPurchaseData.from_dict(
        json.loads(task.get_variable("offer_purchase_data")))
    tickets = json.loads(str(task.get_variable("tickets")))

    # Connects to PostgreSQL to get the offer match information
    Session = sessionmaker(bind=create_sql_engine())
    session = Session()
    offer_match: OfferMatch = session.query(OfferMatch).get(
        {"offer_code": offer_purchase_data.offer_code})

    # Identifies the Travel Company to contact, choosing the one nearer to the user's address.
    travel_company_to_contact = min(distances,
                                    key=lambda tc: tc.get("distance"))

    # Creates the SOAP Client and the datetime when then transfer will be booked for.
    # We need to replace the port for accessing the web server serving the WSDL interface.
    wsdl_url = travel_company_to_contact.get("company").replace(
        ":8080", ":8000") + "/travel_company.wsdl"
    soap_client = Client(wsdl=wsdl_url)

    outbound_departure_transfer_datetime = offer_match.outbound_flight.departure_datetime - timedelta(
        hours=4)
    comeback_arrival_transfer_datetime = offer_match.comeback_flight.arrival_datetime + timedelta(
        minutes=10)

    try:
        soap_response = soap_client.service.buyTransfers(
            departure_transfer_datetime=outbound_departure_transfer_datetime.
            strftime("%Y-%m-%dT%H:%M:%S"),
            customer_address=str(offer_purchase_data.address),
            airport_code=offer_match.outbound_flight.departure_airport_code,
            customer_name=
            f"{offer_purchase_data.name} {offer_purchase_data.surname}",
            arrival_transfer_datetime=comeback_arrival_transfer_datetime.
            strftime("%Y-%m-%dT%H:%M:%S"))
        tickets["transfers"] = [soap_response]
        return task.complete(global_variables={"tickets": json.dumps(tickets)})
    except Fault:
        return task.failure("Book ticket",
                            "Failure in booking ticket from travel company",
                            max_retries=5,
                            retry_timeout=10)
async def get_iovation_data(task: ExternalTask):
    # put the business logic here
    logger.info(f"get_iovation_data: {task}")
    failure = random_true()
    if failure:
        return task.failure("iovation task failed",
                            "failed iovation task details", 3, 5000)

    return task.complete({
        "success": True,
        "iovation_task_completed_on": str(datetime.now())
    })
async def get_sentilink_data(task: ExternalTask):
    # put the business logic here
    logger.info(f"get_sentilink_data: {task}")
    is_bpmn_error = random_true()

    if is_bpmn_error:
        return task.bpmn_error("SentlinkDetectedFraud")

    return task.complete({
        "success": True,
        "sentilink_task_completed_on": str(datetime.now())
    })
    def test_task_failure(self):
        task = ExternalTask({"id": "1", "topicName": "my_topic"})
        expected_task_result = TaskResult.failure(task,
                                                  error_message="unknown task failure", error_details="unknown error",
                                                  retries=3, retry_timeout=30000)

        external_task_client = ExternalTaskClient(worker_id=1)
        responses.add(responses.POST, external_task_client.get_task_failure_url(task.get_task_id()),
                      status=HTTPStatus.NO_CONTENT)
        executor = ExternalTaskExecutor(worker_id=1, external_task_client=external_task_client)

        actual_task_result = executor.execute_task(task, self.task_failure_action)
        self.assertEqual(str(expected_task_result), str(actual_task_result))
def validate_image(task: ExternalTask):
    """
    To simulate BPMN/Failure/Success, this handler uses image name variable (to be passed when launching the process)
    """
    log_context = {
        "WORKER_ID": task.get_worker_id(),
        "TASK_ID": task.get_task_id(),
        "TOPIC": task.get_topic_name()
    }

    log_with_context("executing validate_image", log_context)
    img_name = task.get_variable('imgName')

    if "poor" in img_name:
        return task.bpmn_error(
            "POOR_QUALITY_IMAGE", "Image quality is bad", {
                "img_rejection_code": "POOR_QUALITY_CODE_XX",
                "img_rejection_reason": f"Image quality must be at least GOOD"
            })
    elif "jpg" in img_name:
        return task.complete({"img_approved": True})
    elif "corrupt" in img_name:
        return task.failure("Cannot validate image", "image is corrupted", 0,
                            default_config.get("retryTimeout"))
    else:
        return task.bpmn_error(
            "INVALID_IMAGE", "Image extension must be jpg", {
                "img_rejection_code": "INVALID_IMG_NAME",
                "img_rejection_reason": f"Image name {img_name} is invalid"
            })
Ejemplo n.º 28
0
def check_distance_house_airport(task: ExternalTask) -> TaskResult:
    """
    Checks if the distance (that we got when contacting the Geographical Distance service)
    is congruent with the transfer bundle.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("check_distance_house_airport")

    GEOGRAPHICAL_DISTACE_SERVICE = environ.get(
        "GEOGRAPHICAL_DISTACE_SERVICE", "http://geographical_distances:8080")

    offer_purchase_data = OfferPurchaseData.from_dict(
        json.loads(task.get_variable("offer_purchase_data")))

    # Connects to postgreSQL and get the offer purchased
    Session = sessionmaker(bind=create_sql_engine())
    session = Session()
    offer_match: OfferMatch = session.query(OfferMatch).get(
        {"offer_code": offer_purchase_data.offer_code})

    # Finds the name (used for the airport) of the departure airport
    airports_file = open("./camundaworkers/airports.csv", 'r')
    airports = csv.reader(airports_file)
    airport_address = None
    for row in airports:
        if row[4] == offer_match.outbound_flight.departure_airport_code:
            airport_address = row[1]
    airports_file.close()

    # Failure case: the airport cannot be found in the CSV.
    if not airport_address:
        logger.error(
            f"Cannot find airport associated with: {offer_match.outbound_flight.departure_airport_code}"
        )
        return task.complete(global_variables={
            "distance": "35"
        })  # 35 > 30, then the transfer won't be booked.

    request = {
        "address_1": airport_address,
        "address_2": str(offer_purchase_data.address)
    }

    distance_request = requests.post(GEOGRAPHICAL_DISTACE_SERVICE +
                                     "/distance",
                                     json=request)
    return task.complete(global_variables={"distance": distance_request.text})
    def test_failure_returns_failure_task_result(self):
        task = ExternalTask(context={})
        task_result = task.failure(error_message="unknown error", error_details="error details here",
                                   max_retries=3, retry_timeout=1000)

        self.assertEqual(task, task_result.get_task())
        self.assertEqual(task_result, task.get_task_result())

        self.assertFalse(task_result.is_success())
        self.assertTrue(task_result.is_failure())
        self.assertFalse(task_result.is_bpmn_error())

        self.assertEqual("unknown error", task_result.error_message)
        self.assertEqual("error details here", task_result.error_details)
        self.assertEqual(3, task_result.retries)
        self.assertEqual(1000, task_result.retry_timeout)
def verify_condition_for_travel_booking(task: ExternalTask) -> TaskResult:
    """
    Verifies the price condition for the transfer bundle.
    :param task: the current task instance
    :return: the task result
    """
    logger = get_logger()
    logger.info("verify_condition_for_travel_booking")

    total_amount = float(task.get_variable("total_amount"))

    if total_amount >= 1000:
        return task.complete(
            global_variables={"can_book_travel_company": True})
    else:
        return task.complete(
            global_variables={"can_book_travel_company": False})