Skip to content

Commit 95c5ee3

Browse files
committed
add initial python library project structure
0 parents  commit 95c5ee3

File tree

8 files changed

+437
-0
lines changed

8 files changed

+437
-0
lines changed

.gitignore

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
share/python-wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
MANIFEST
28+
29+
# PyInstaller
30+
# Usually these files are written by a python script from a template
31+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
32+
*.manifest
33+
*.spec
34+
35+
# Installer logs
36+
pip-log.txt
37+
pip-delete-this-directory.txt
38+
39+
# Unit test / coverage reports
40+
htmlcov/
41+
.tox/
42+
.nox/
43+
.coverage
44+
.coverage.*
45+
.cache
46+
nosetests.xml
47+
coverage.xml
48+
*.cover
49+
*.py,cover
50+
.hypothesis/
51+
.pytest_cache/
52+
cover/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
.pybuilder/
76+
target/
77+
78+
# Jupyter Notebook
79+
.ipynb_checkpoints
80+
81+
# IPython
82+
profile_default/
83+
ipython_config.py
84+
85+
# pyenv
86+
# For a library or package, you might want to ignore these files since the code is
87+
# intended to run in multiple environments; otherwise, check them in:
88+
# .python-version
89+
90+
# pipenv
91+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94+
# install all needed dependencies.
95+
#Pipfile.lock
96+
97+
# poetry
98+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99+
# This is especially recommended for binary packages to ensure reproducibility, and is more
100+
# commonly ignored for libraries.
101+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102+
#poetry.lock
103+
104+
# pdm
105+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106+
#pdm.lock
107+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108+
# in version control.
109+
# https://pdm.fming.dev/#use-with-ide
110+
.pdm.toml
111+
112+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113+
__pypackages__/
114+
115+
# Celery stuff
116+
celerybeat-schedule
117+
celerybeat.pid
118+
119+
# SageMath parsed files
120+
*.sage.py
121+
122+
# Environments
123+
.env
124+
.venv
125+
env/
126+
venv/
127+
ENV/
128+
env.bak/
129+
venv.bak/
130+
131+
# Spyder project settings
132+
.spyderproject
133+
.spyproject
134+
135+
# Rope project settings
136+
.ropeproject
137+
138+
# mkdocs documentation
139+
/site
140+
141+
# mypy
142+
.mypy_cache/
143+
.dmypy.json
144+
dmypy.json
145+
146+
# Pyre type checker
147+
.pyre/
148+
149+
# pytype static type analyzer
150+
.pytype/
151+
152+
# Cython debug symbols
153+
cython_debug/
154+
155+
# PyCharm
156+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158+
# and can be added to the global gitignore or merged into this file. For a more nuclear
159+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
160+
.idea/
161+
*.iml

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Flowable External Worker Library for Python
2+

docker-compose.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
version: '3.4'
2+
3+
services:
4+
database:
5+
image: postgres:14.2
6+
environment:
7+
POSTGRES_DB: flowable
8+
POSTGRES_USER: flowable
9+
POSTGRES_PASSWORD: flowable
10+
volumes:
11+
- data_db:/var/lib/postgresql/data
12+
13+
elasticsearch:
14+
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.3
15+
environment:
16+
discovery.type: single-node
17+
node.name: flowable-node-01
18+
cluster.name: flowable-cluster
19+
xpack.security.enabled: "false"
20+
volumes:
21+
- data_es:/usr/share/elasticsearch/data
22+
ulimits:
23+
memlock:
24+
soft: -1
25+
hard: -1
26+
27+
flowable-work:
28+
image: artifacts.flowable.com/flowable/flowable-work:3.14.0
29+
environment:
30+
flowable.content.storage.root-folder: /content-storage
31+
server.servlet.context-path: /
32+
spring.elasticsearch.uris: http://elasticsearch:9200
33+
spring.datasource.driver-class-name: org.postgresql.Driver
34+
spring.datasource.url: jdbc:postgresql://database:5432/flowable
35+
spring.datasource.username: flowable
36+
spring.datasource.password: flowable
37+
ports:
38+
- 8090:8080
39+
volumes:
40+
- data_work:/content-storage
41+
depends_on:
42+
- database
43+
- elasticsearch
44+
user: root
45+
deploy:
46+
restart_policy:
47+
condition: on-failure
48+
delay: 5s
49+
max_attempts: 3
50+
window: 120s
51+
52+
flowable-design:
53+
image: artifacts.flowable.com/flowable/flowable-design:3.14.0
54+
environment:
55+
flowable.design.remote.authentication.user: admin
56+
flowable.design.remote.authentication.password: test
57+
flowable.design.remote.idm-url: http://flowable-work:8080
58+
flowable.design.deployment-api-url: http://flowable-work:8080/app-api
59+
flowable.design.undeployment-api-url: http://flowable-work:8080/platform-api/app-deployments
60+
flowable.design.db-store-enabled: "true"
61+
server.servlet.context-path: /
62+
spring.datasource.driver-class-name: org.postgresql.Driver
63+
spring.datasource.url: jdbc:postgresql://database:5432/flowable
64+
spring.datasource.username: flowable
65+
spring.datasource.password: flowable
66+
ports:
67+
- 8091:8080
68+
depends_on:
69+
- database
70+
71+
flowable-control:
72+
image: artifacts.flowable.com/flowable/flowable-control:3.14.0
73+
environment:
74+
flowable.common.app.idm-admin.user: admin
75+
flowable.common.app.idm-admin.password: test
76+
flowable.control.app.cluster-config.server-address: http://flowable-work
77+
flowable.control.app.cluster-config.port: 8080
78+
flowable.control.app.cluster-config.context-root: /
79+
flowable.control.app.cluster-config.password: test
80+
server.servlet.context-path: /
81+
spring.datasource.driver-class-name: org.postgresql.Driver
82+
spring.datasource.url: jdbc:postgresql://database:5432/flowable
83+
spring.datasource.username: flowable
84+
spring.datasource.password: flowable
85+
ports:
86+
- 8092:8080
87+
depends_on:
88+
- database
89+
90+
volumes:
91+
data_db:
92+
data_es:
93+
data_work:

flowableexternalworker/__init__.py

Whitespace-only changes.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
from datetime import datetime
2+
from typing import Generic, TypeVar, Callable
3+
4+
import requests
5+
from requests import Session
6+
from requests.auth import AuthBase
7+
8+
T = TypeVar('T')
9+
10+
11+
class ListResult(Generic[T]):
12+
def __init__(self, data, total, start, sort, order, size) -> None:
13+
self.data: list[T] = data
14+
self.total: int = total
15+
self.start: int = start
16+
self.sort: str = sort
17+
self.order: str = order
18+
self.size: int = size
19+
20+
21+
class FlowableRestException(Exception):
22+
def __init__(self, status_code, content=None):
23+
if content is not None and content != '':
24+
super().__init__("Failed to call Flowable with status code " + str(status_code) + " and content '" + content + "'")
25+
else:
26+
super().__init__("Failed to call Flowable with status code " + str(status_code))
27+
28+
29+
class ExternalWorkerJob(object):
30+
def __init__(
31+
self,
32+
id,
33+
url,
34+
correlation_id,
35+
process_instance_id,
36+
execution_id,
37+
scope_id,
38+
sub_scope_id,
39+
scope_definition_id,
40+
scope_type,
41+
element_id,
42+
element_name,
43+
retries,
44+
exception_message,
45+
due_date,
46+
create_time,
47+
tenant_id,
48+
lock_owner,
49+
lock_expiration_time
50+
):
51+
self.id: str = id
52+
self.url: str = url
53+
self.correlation_id: str = correlation_id
54+
self.process_instance_id: str = process_instance_id
55+
self.execution_id: str = execution_id
56+
self.scope_id: str = scope_id
57+
self.sub_scope_id: str = sub_scope_id
58+
self.scope_definition_id: str = scope_definition_id
59+
self.scope_type: str = scope_type
60+
self.element_id: str = element_id
61+
self.element_name: str = element_name
62+
self.retries: int = retries
63+
self.exception_message: str = exception_message
64+
self.due_date: datetime = due_date
65+
self.create_time: datetime = create_time
66+
self.tenant_id: str = tenant_id
67+
self.lock_owner: str = lock_owner
68+
self.lock_expiration_time: datetime = lock_expiration_time
69+
70+
71+
class FlowableExternalWorkerRestClient(object):
72+
"""
73+
Create a REST API Client for the Flowable External Worker.
74+
Used to communicate with the REST API and exchange information.
75+
"""
76+
77+
def __init__(self, flowable_host: str, auth: AuthBase = None, customize_session: Callable[[Session], None] = lambda session: None) -> None:
78+
self.flowable_host: str = flowable_host
79+
self.request_session: Session = requests.Session()
80+
if auth is not None:
81+
self.request_session.auth = auth
82+
if customize_session is not None:
83+
customize_session(self.request_session)
84+
85+
def list_jobs(self) -> ListResult[ExternalWorkerJob]:
86+
r = self.request_session.get(self.flowable_host + '/external-job-api/jobs')
87+
88+
if r.status_code != 200:
89+
raise FlowableRestException(r.status_code, r.text)
90+
91+
result = r.json()
92+
jobs = list(map(lambda o: ExternalWorkerJob(
93+
o.get('id'),
94+
o.get('url'),
95+
o.get('correlationId'),
96+
o.get('processInstanceId'),
97+
o.get('executionId'),
98+
o.get('scopeId'),
99+
o.get('subScopeId'),
100+
o.get('scopeDefinitionId'),
101+
o.get('scopeType'),
102+
o.get('elementId'),
103+
o.get('elementName'),
104+
o.get('retries'),
105+
o.get('exceptionMessage'),
106+
parse_date_time(o.get('dueDate')),
107+
parse_date_time(o.get('createTime')),
108+
o.get('tenantId'),
109+
o.get('lockOwner'),
110+
parse_date_time(o.get('lockExpirationTime')),
111+
), result.get('data')))
112+
113+
return ListResult(
114+
jobs,
115+
result.get('total'),
116+
result.get('start'),
117+
result.get('sort'),
118+
result.get('order'),
119+
result.get('size')
120+
)
121+
122+
123+
def parse_date_time(date):
124+
return datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ") if date is not None else None

setup.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from setuptools import find_packages, setup
2+
3+
setup(
4+
name='flowableexternalworker',
5+
packages=find_packages(include=['flowableexternalworker']),
6+
version='0.0.1',
7+
description='Flowable External Worker Library to write code using an external worker.',
8+
author='Flowable',
9+
license='',
10+
install_requires=['requests'],
11+
setup_requires=['pytest-runner'],
12+
tests_require=['pytest'],
13+
test_suite='tests',
14+
)

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)