Skip to content

Commit

Permalink
add support for .robot files
Browse files Browse the repository at this point in the history
  • Loading branch information
vzickner committed Sep 23, 2024
1 parent fdb8783 commit 6fab0f3
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 8 deletions.
26 changes: 26 additions & 0 deletions robocorp/flowable/robocorp_client/API.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import json
import os

from robot.api.deco import library, keyword


@library
class API:

result = {}

@keyword
def flw_input(self, name: str):
variables = os.environ["FLOWABLE_INPUT_VARIABLES"]
obj = json.loads(variables)
return obj[name]

@keyword
def flw_output(self, name: str, value: any):
output_file_name = os.environ.get("FLOWABLE_OUTPUT_FILE")
self.result[name] = value
print('assign ' + name + '=' + value)
if output_file_name is not None:
with open(output_file_name, "w") as f:
f.write(json.dumps(self.result))
f.close()
8 changes: 5 additions & 3 deletions robocorp/flowable/robocorp_client/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

from flowable.external_worker_client import ExternalWorkerClient
from flowable.external_worker_client.cloud_token import FlowableCloudToken
from flowable.robocorp_client.robocorp_handler import RobocorpActionHandler, RobocorpTaskHandler
from flowable.robocorp_client.robocorp_handler import RobocorpActionHandler, RobocorpTaskHandler, RobocorpRobotHandler

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Flowable Robocorp Client")

parser.add_argument('topic', help='Topic of the Robocorp Action to listen to')
parser.add_argument('mode', type=str, choices=['action', 'task'], help='Type of robocorp action/task')
parser.add_argument('mode', type=str, choices=['action', 'task', 'robot'], help='Type of robocorp action/task/robot')
parser.add_argument('path', type=str, help='The directory or file with the actions/tasks to run.')
parser.add_argument('--flowable-host', type=str, default='https://trial.flowable.com', help='URL of Flowable Work')
parser.add_argument('--flowable-token', type=str, help='Bearer Token, can be used for example with the Flowable Trial')
Expand All @@ -32,6 +32,8 @@

if args.mode == 'action':
robocorp_job_handler = RobocorpActionHandler(robocorp_action_file)
else:
elif args.mode == 'task':
robocorp_job_handler = RobocorpTaskHandler(robocorp_action_file)
else:
robocorp_job_handler = RobocorpRobotHandler(robocorp_action_file)
subscription = client.subscribe(topic, robocorp_job_handler.handle_task)
7 changes: 5 additions & 2 deletions robocorp/flowable/robocorp_client/call_robocorp.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import os
import subprocess
import sys


def call_robocorp(args, mod_name='robocorp.actions'):
def call_robocorp(args, mod_name, env=None):
if env is None:
env = os.environ.copy()
call_args = [sys.executable, "-m", mod_name]
call_args.extend(args)
try:
return subprocess.run(call_args, capture_output=True, text=True)
return subprocess.run(call_args, capture_output=True, text=True, env=env)
except subprocess.CalledProcessError as exc:
print("ERROR: failed to call robocorp", exc.returncode, exc.output)

55 changes: 52 additions & 3 deletions robocorp/flowable/robocorp_client/robocorp_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import ast
import json
import os
import shlex
import tempfile

from flowable.external_worker_client import ExternalWorkerAcquireJobResponse, WorkerResultBuilder
from flowable.robocorp_client.call_robocorp import call_robocorp
Expand All @@ -18,6 +20,18 @@ def extract_action_and_parameters(job):
return action, params


def extract_action_and_parameters_map(job):
action = None
params = {}
for variable in job.variables:
if variable.name == '__robocorpTaskName':
action = variable.value
else:
if variable.value is not None:
params[variable.name] = variable.value
return action, params


def extract_result(results):
result_item = None
lines = results.strip().splitlines()
Expand All @@ -35,8 +49,7 @@ def create_output_dir(job):
return 'output/' + job.id + '-' + (job.scope_id or job.process_instance_id) + '-' + job.element_id


def add_variables_to_result(work_result, result):
json_result = ast.literal_eval(result)
def add_variables_to_result(work_result, json_result):

if isinstance(json_result, dict):
for key in json_result:
Expand Down Expand Up @@ -78,7 +91,8 @@ def handle_task(self, job: ExternalWorkerAcquireJobResponse, worker_result_build
result = extract_result(results.stdout)
print('---> Job execution done for "' + job.id + '" with result ' + result + '". Output saved to ' + output_dir, robocorp_args)
work_result = worker_result_builder.success()
return add_variables_to_result(work_result, result)
json_result = ast.literal_eval(result)
return add_variables_to_result(work_result, json_result)


class RobocorpTaskHandler:
Expand All @@ -100,3 +114,38 @@ def handle_task(self, job: ExternalWorkerAcquireJobResponse, worker_result_build
return worker_result_builder.failure().error_message('failed with status code ' + str(results.returncode)).error_details(results.stderr + results.stdout)
print('---> Job execution done for "' + job.id + '". Output saved to ' + output_dir, robocorp_args)
return worker_result_builder.success()


class RobocorpRobotHandler:
def __init__(self, robocorp_action_file: str):
self.robocorp_action_file = robocorp_action_file

def handle_task(self, job: ExternalWorkerAcquireJobResponse, worker_result_builder: WorkerResultBuilder):
action, params = extract_action_and_parameters_map(job)
if action is None:
return worker_result_builder.failure().error_message('failed to find robocorp action name')

output_dir = create_output_dir(job)
robocorp_args = ['--rpa', '--name', action.__str__(), '--report', 'NONE', '--outputdir', output_dir, self.robocorp_action_file]
print('---> Execute job "' + job.id + '"', robocorp_args)
tmp = tempfile.NamedTemporaryFile(delete=False)
tmp.close()
new_env = os.environ.copy()
new_env["FLOWABLE_INPUT_VARIABLES"] = json.dumps(params)
new_env["FLOWABLE_OUTPUT_FILE"] = tmp.name
results = call_robocorp(robocorp_args, mod_name='robot', env=new_env)

if results.returncode != 0:
print('---> Job execution failed for "' + job.id + '". Output saved to ' + output_dir)
os.unlink(tmp.name)
return worker_result_builder.failure().error_message('failed with status code ' + str(results.returncode)).error_details(results.stderr + results.stdout)
print('---> Job execution done for "' + job.id + '". Output saved to ' + output_dir, robocorp_args)

with open(tmp.name, 'r') as content_file:
content = json.loads(content_file.read())
os.unlink(tmp.name)

if content is None:
return worker_result_builder.success()
else:
return add_variables_to_result(worker_result_builder.success(), content)

0 comments on commit 6fab0f3

Please sign in to comment.