Skip to content

rename module to rpaframework #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ jobs:
working-directory: ./external-worker/
- run: python -m unittest discover
working-directory: ./external-worker/
test-robocorp-client:
test-rpa-framework-client:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12.0"]
python-version: ["3.11.0"]

steps:
- uses: actions/checkout@v4
Expand All @@ -46,7 +46,7 @@ jobs:
- run: python setup.py install
working-directory: ./external-worker/
- run: pip install -e ".[testing]"
working-directory: ./robocorp/
- run: cp -a external-worker/flowable/external_worker_client robocorp/flowable/ # we need to copy this module over, since it's only searching locally for modules
working-directory: ./rpaframework/
- run: cp -a external-worker/flowable/external_worker_client rpaframework/flowable/ # we need to copy this module over, since it's only searching locally for modules
- run: python -m unittest discover
working-directory: ./robocorp/
working-directory: ./rpaframework/
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
packages-dir: external-worker/dist/
verbose: true

build-robocorp-client:
build-rpa-framework-client:
runs-on: ubuntu-latest
environment: release
permissions:
Expand All @@ -49,11 +49,11 @@ jobs:
- run: source venv/bin/activate
- run: pip install setuptools
- run: python setup.py sdist
working-directory: ./robocorp/
working-directory: ./rpaframework/
- run: pip wheel --no-deps . -w dist
working-directory: ./robocorp/
working-directory: ./rpaframework/
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: robocorp/dist/
packages-dir: rpaframework/dist/
verbose: true
10 changes: 5 additions & 5 deletions .github/workflows/test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Publish Package to Test PyPi
on:
create:
tags:
- v*
- 'v*'

jobs:
build-external-worker-client:
Expand Down Expand Up @@ -34,7 +34,7 @@ jobs:
packages-dir: external-worker/dist/
verbose: true

build-robocorp-client:
build-rpa-framework-client:
runs-on: ubuntu-latest
environment: release
permissions:
Expand All @@ -52,12 +52,12 @@ jobs:
- run: source venv/bin/activate
- run: pip install setuptools
- run: python setup.py sdist
working-directory: ./robocorp/
working-directory: ./rpaframework/
- run: pip wheel --no-deps . -w dist
working-directory: ./robocorp/
working-directory: ./rpaframework/
- name: Publish package distributions to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
packages-dir: robocorp/dist/
packages-dir: rpaframework/dist/
verbose: true
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ This is a collection of multiple Flowable Python clients.
Currently, the project consists out of:

* [External Worker Client](./external-worker): A library to connect custom application code to Flowable with the external worker functionality
* [Robocorp Client](./robocorp-client): A python module to execute Robocorp actions and tasks with the Flowable Robocorp task (based on the external worker).
* [RPA Framework Client](./rpaframework): A python module to execute RPA Framework tasks with the Flowable RPA Framework task (based on the external worker).
74 changes: 0 additions & 74 deletions robocorp/README.md

This file was deleted.

1 change: 1 addition & 0 deletions rpaframework/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output
54 changes: 54 additions & 0 deletions rpaframework/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Flowable RPA Framework Client

[License:
![license](https://img.shields.io/hexpm/l/plug.svg)](https://github.com/flowable/flowable-external-client-python/blob/main/LICENSE)

![Flowable Actions CI](https://github.com/flowable/flowable-external-client-python/actions/workflows/main.yml/badge.svg?branch=main)

This is a python module to connect a Flowable installation through an external worker with a RPA framework task.
This allows to execute an RPA framework task from a business process with BPMN or a case diagram CMMN.
The application is started with a task definition from RPA framework and connects to an external worker topic.
When using the RPA framework task in Flowable, it is possible to specify a task name.
In the task, the specific task specified will be executed and the result will be sent back to the process.
It is required that the variables in the diagram match exactly the variables inside the RPA framework task.

## Installation

To install the Flowable RPA framework client, execute the following command:

```
pip install flowable.rpaframework-client
```

## Sample

The following [diagram BPMN](docs/rpaframeworkExample.bpmn) illustrates a basic usage of the RPA framework task:

![Simple BPMN diagram with a start event, a RPA Framework task and an end event](docs/rpaframeworkExample.png)

The following example `get-weather-forecast.py` can be used as an RPA framework task:
```

```

The RPA Framework worker can be started with the following command:
```sh
python -m flowable.rpaframework_client --flowable-token <your-token> myTopic myTask.robot
```

You can request `<your-token>` at the [Flowable Trial](https://trial.flowable.com/work/) by clicking at the bottom left on your user and go to Settings.
Once in the settings, choose "Access Token" and press the button "New token".
After providing name and the validity, the token is generated and can be set.

Once the module is started and a process instance is created, it will automatically execute the RPA framework action and returns the result to Flowable.
The Python module will keep running until stopped to process potential additional jobs.

## Authentication

Next to the cloud authentication, it's possible to authenticate against other hosts.
The hostname can be specified with `--flowable-host`.

For the authentication there are two possibilities:

1. Providing a bearer token with the `--flowable-token <token>` attribute.
2. Providing basic authentication with `--flowable-username <username>` and `--flowable-password <password>`
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
<extensionElements>
<flowable:externalWorkerInParameter source="city" target="city"></flowable:externalWorkerInParameter>
<flowable:externalWorkerInParameter source="days" target="days"></flowable:externalWorkerInParameter>
<flowable:externalWorkerInParameter sourceExpression="get_weather_forecast" target="__robocorpTaskName"></flowable:externalWorkerInParameter>
<flowable:externalWorkerInParameter sourceExpression="get_weather_forecast" target="__rpaFrameworkTaskName"></flowable:externalWorkerInParameter>
<flowable:externalWorkerOutParameter source="temperature" target="temperature"></flowable:externalWorkerOutParameter>
<design:stencilid><![CDATA[RobocorpTask]]></design:stencilid>
<design:stencilid><![CDATA[RpaFrameworkTask]]></design:stencilid>
<design:stencilsuperid><![CDATA[ExternalWorkerTask]]></design:stencilsuperid>
</extensionElements>
</serviceTask>
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ def flw_input(self, name: str):
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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

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, RobocorpRobotHandler
from flowable.rpaframework_client.rpaframework_handler import RobocorpActionHandler, RobocorpTaskHandler, RpaframeworkRobotHandler

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

parser.add_argument('topic', help='Topic of the Robocorp Action to listen to')
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('topic', help='Topic of the client to listen to')
parser.add_argument('path', type=str, help='The directory or file with the RPA framework task to run.')
parser.add_argument('--mode', type=str, choices=['robot', 'action', 'task'], default='robot', help='Type of rpaframework robot or robocorp action or task')
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')
parser.add_argument('--flowable-username', type=str, help='Username for Flowable Work when using Basic Authentication')
Expand All @@ -20,7 +20,6 @@
args = parser.parse_args()

topic = args.topic
robocorp_action_file = args.path
auth = None
if args.flowable_token:
auth = FlowableCloudToken(args.flowable_token)
Expand All @@ -31,9 +30,9 @@
client = ExternalWorkerClient(args.flowable_host, auth=auth)

if args.mode == 'action':
robocorp_job_handler = RobocorpActionHandler(robocorp_action_file)
job_handler = RobocorpActionHandler(args.path)
elif args.mode == 'task':
robocorp_job_handler = RobocorpTaskHandler(robocorp_action_file)
job_handler = RobocorpTaskHandler(args.path)
else:
robocorp_job_handler = RobocorpRobotHandler(robocorp_action_file)
subscription = client.subscribe(topic, robocorp_job_handler.handle_task)
job_handler = RpaframeworkRobotHandler(args.path)
subscription = client.subscribe(topic, job_handler.handle_task)
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import sys


def call_robocorp(args, mod_name, env=None):
def call_python_module(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, env=env)
except subprocess.CalledProcessError as exc:
print("ERROR: failed to call robocorp", exc.returncode, exc.output)
print("ERROR: failed to call rpa framework", exc.returncode, exc.output)

Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import tempfile

from flowable.external_worker_client import ExternalWorkerAcquireJobResponse, WorkerResultBuilder
from flowable.robocorp_client.call_robocorp import call_robocorp
from flowable.rpaframework_client.call_python_module import call_python_module


def extract_action_and_parameters(job):
action = None
params = []
for variable in job.variables:
if variable.name == '__robocorpTaskName':
if variable.name == '__rpaFrameworkTaskName':
action = variable.value
else:
if variable.value is not None:
Expand All @@ -24,7 +24,7 @@ def extract_action_and_parameters_map(job):
action = None
params = {}
for variable in job.variables:
if variable.name == '__robocorpTaskName':
if variable.name == '__rpaFrameworkTaskName':
action = variable.value
else:
if variable.value is not None:
Expand Down Expand Up @@ -84,7 +84,7 @@ def handle_task(self, job: ExternalWorkerAcquireJobResponse, worker_result_build
robocorp_args = ['run', self.robocorp_action_file, '-a' + action.__str__(), '--log-output-to-stdout=json', '-o' + output_dir, '--']
robocorp_args.extend(params)
print('---> Execute job "' + job.id + '"', robocorp_args)
results = call_robocorp(robocorp_args, mod_name='robocorp.actions')
results = call_python_module(robocorp_args, mod_name='robocorp.actions')
if results.returncode != 0:
print('---> Job execution failed for "' + job.id + '". Output saved to ' + output_dir)
return worker_result_builder.failure().error_message('failed with status code ' + str(results.returncode)).error_details(results.stderr + results.stdout)
Expand All @@ -108,38 +108,38 @@ def handle_task(self, job: ExternalWorkerAcquireJobResponse, worker_result_build
robocorp_args = ['run', self.robocorp_action_file, '-t' + action.__str__(), '-o' + output_dir, '--']
robocorp_args.extend(params)
print('---> Execute job "' + job.id + '"', robocorp_args)
results = call_robocorp(robocorp_args, mod_name='robocorp.tasks')
results = call_python_module(robocorp_args, mod_name='robocorp.tasks')
if results.returncode != 0:
print('---> Job execution failed for "' + job.id + '". Output saved to ' + output_dir)
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
class RpaframeworkRobotHandler:
def __init__(self, rpaframework_action_file: str):
self.rpaframework_action_file = rpaframework_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')
return worker_result_builder.failure().error_message('failed to find rpaframework 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)
rpaframework_args = ['--rpa', '--name', action.__str__(), '--report', 'NONE', '--outputdir', output_dir, self.rpaframework_action_file]
print('---> Execute job "' + job.id + '"', rpaframework_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)
results = call_python_module(rpaframework_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)
print('---> Job execution done for "' + job.id + '". Output saved to ' + output_dir, rpaframework_args)

with open(tmp.name, 'r') as content_file:
content = json.loads(content_file.read())
Expand Down
Loading
Loading