Merge branch 'main' of github.com:sartography/spiff-arena into send_filters
This commit is contained in:
commit
1584f52a18
41
bin/run_pyl
41
bin/run_pyl
|
@ -12,25 +12,54 @@ python_projects=(
|
|||
spiffworkflow-backend
|
||||
)
|
||||
|
||||
function run_fix_docstrings() {
|
||||
fix_python_docstrings $(get_top_level_directories_containing_python_files)
|
||||
react_projects=(
|
||||
spiffworkflow-frontend
|
||||
)
|
||||
|
||||
function get_python_dirs() {
|
||||
(git ls-tree -r HEAD --name-only | grep -E '\.py$' | awk -F '/' '{print $1}' | sort | uniq | grep -v '\.' | grep -Ev '^(bin|migrations)$') || echo ''
|
||||
}
|
||||
|
||||
function run_autoflake() {
|
||||
if ! command -v autoflake8 >/dev/null ; then
|
||||
pip install autoflake8
|
||||
asdf reshim python
|
||||
fi
|
||||
|
||||
if ! command -v autopep8 >/dev/null ; then
|
||||
pip install autopep8
|
||||
asdf reshim python
|
||||
fi
|
||||
|
||||
python_dirs=$(get_python_dirs)
|
||||
python_files=$(find $python_dirs -type f -name "*.py" ! -name '.null-ls*' ! -name '_null-ls*')
|
||||
|
||||
autoflake8 --in-place --remove-unused-variables --remove-duplicate-keys --expand-star-imports --exit-zero-even-if-changed $python_files
|
||||
autoflake --in-place --remove-all-unused-imports $python_files
|
||||
autopep8 --in-place $python_files
|
||||
}
|
||||
|
||||
function run_pre_commmit() {
|
||||
poetry run pre-commit run --verbose --all-files
|
||||
}
|
||||
|
||||
for react_project in "${react_projects[@]}" ; do
|
||||
pushd "$react_project"
|
||||
npm run lint:fix
|
||||
popd
|
||||
done
|
||||
|
||||
for python_project in "${python_projects[@]}" ; do
|
||||
pushd "$python_project"
|
||||
run_fix_docstrings || run_fix_docstrings
|
||||
run_autoflake || run_autoflake
|
||||
popd
|
||||
done
|
||||
run_pre_commmit || run_pre_commmit
|
||||
|
||||
for python_project in "${python_projects[@]}"; do
|
||||
pushd "$python_project"
|
||||
poet i
|
||||
poet mypy
|
||||
poet test
|
||||
poetry install
|
||||
poetry run mypy $(get_python_dirs)
|
||||
poetry run coverage run --parallel -m pytest
|
||||
popd
|
||||
done
|
||||
|
|
|
@ -98,7 +98,7 @@ python-versions = ">=3.5"
|
|||
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
|
||||
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
|
||||
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
|
||||
tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
|
||||
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "Babel"
|
||||
|
@ -271,7 +271,7 @@ optional = false
|
|||
python-versions = ">=3.6.0"
|
||||
|
||||
[package.extras]
|
||||
unicode_backport = ["unicodedata2"]
|
||||
unicode-backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "classify-imports"
|
||||
|
@ -643,7 +643,7 @@ werkzeug = "*"
|
|||
type = "git"
|
||||
url = "https://github.com/sartography/flask-bpmn"
|
||||
reference = "main"
|
||||
resolved_reference = "6f6762ec83bb6eec24f7cc799d4d5fa7867c7474"
|
||||
resolved_reference = "860f2387bebdaa9220e9fbf6f8fa7f74e805d0d4"
|
||||
|
||||
[[package]]
|
||||
name = "Flask-Cors"
|
||||
|
@ -1517,7 +1517,7 @@ urllib3 = ">=1.21.1,<1.27"
|
|||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests-toolbelt"
|
||||
|
@ -1630,7 +1630,7 @@ falcon = ["falcon (>=1.4)"]
|
|||
fastapi = ["fastapi (>=0.79.0)"]
|
||||
flask = ["blinker (>=1.1)", "flask (>=0.11)"]
|
||||
httpx = ["httpx (>=0.16.0)"]
|
||||
pure_eval = ["asttokens", "executing", "pure-eval"]
|
||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||
pyspark = ["pyspark (>=2.4.4)"]
|
||||
quart = ["blinker (>=1.1)", "quart (>=0.16.1)"]
|
||||
rq = ["rq (>=0.6)"]
|
||||
|
@ -1876,7 +1876,7 @@ lxml = "*"
|
|||
type = "git"
|
||||
url = "https://github.com/sartography/SpiffWorkflow"
|
||||
reference = "main"
|
||||
resolved_reference = "025bc30f27366e06dd1286b7563e4b1cb04c1c46"
|
||||
resolved_reference = "eea53c912984d21a064330c3b3334ac219cb8e18"
|
||||
|
||||
[[package]]
|
||||
name = "SQLAlchemy"
|
||||
|
@ -1894,19 +1894,19 @@ aiomysql = ["aiomysql", "greenlet (!=0.4.17)"]
|
|||
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
|
||||
asyncio = ["greenlet (!=0.4.17)"]
|
||||
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
|
||||
mariadb_connector = ["mariadb (>=1.0.1,!=1.1.2)"]
|
||||
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
|
||||
mssql = ["pyodbc"]
|
||||
mssql_pymssql = ["pymssql"]
|
||||
mssql_pyodbc = ["pyodbc"]
|
||||
mssql-pymssql = ["pymssql"]
|
||||
mssql-pyodbc = ["pyodbc"]
|
||||
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
|
||||
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
|
||||
mysql_connector = ["mysql-connector-python"]
|
||||
mysql-connector = ["mysql-connector-python"]
|
||||
oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"]
|
||||
postgresql = ["psycopg2 (>=2.7)"]
|
||||
postgresql_asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
|
||||
postgresql_psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql_psycopg2cffi = ["psycopg2cffi"]
|
||||
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
|
||||
postgresql-psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql-psycopg2cffi = ["psycopg2cffi"]
|
||||
pymysql = ["pymysql", "pymysql (<1)"]
|
||||
sqlcipher = ["sqlcipher3_binary"]
|
||||
|
||||
|
@ -3056,7 +3056,18 @@ py = [
|
|||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pyasn1 = [
|
||||
{file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"},
|
||||
{file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"},
|
||||
{file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"},
|
||||
{file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"},
|
||||
{file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
|
||||
{file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"},
|
||||
{file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"},
|
||||
{file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"},
|
||||
{file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"},
|
||||
{file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"},
|
||||
{file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"},
|
||||
{file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"},
|
||||
{file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
|
||||
]
|
||||
pycodestyle = [
|
||||
|
|
|
@ -258,7 +258,6 @@ paths:
|
|||
description: The number of models to show per page. Defaults to page 10.
|
||||
schema:
|
||||
type: integer
|
||||
# process_model_list
|
||||
get:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_list
|
||||
summary: Return a list of process models for a given process group
|
||||
|
@ -273,9 +272,10 @@ paths:
|
|||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ProcessModel"
|
||||
# process_model_add
|
||||
|
||||
/process-models/{modified_process_group_id}:
|
||||
post:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_add
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_create
|
||||
summary: Creates a new process model with the given parameters.
|
||||
tags:
|
||||
- Process Models
|
||||
|
@ -371,7 +371,7 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/OkTrue"
|
||||
# process_model_list
|
||||
|
||||
/processes:
|
||||
get:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_list
|
||||
|
@ -976,7 +976,7 @@ paths:
|
|||
items:
|
||||
$ref: "#/components/schemas/Task"
|
||||
|
||||
/process-instance/{process_instance_id}/tasks:
|
||||
/process-instances/{modified_process_model_id}/{process_instance_id}/tasks:
|
||||
parameters:
|
||||
- name: process_instance_id
|
||||
in: path
|
||||
|
|
|
@ -63,6 +63,12 @@ permissions:
|
|||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-groups/*
|
||||
|
||||
process-instance-list:
|
||||
groups: [everybody]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-instances
|
||||
|
||||
# TODO: all uris should really have the same structure
|
||||
finance-admin-group:
|
||||
groups: ["Finance Team"]
|
||||
|
@ -81,3 +87,9 @@ permissions:
|
|||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /*
|
||||
|
||||
invoice-approval-tasks-read:
|
||||
groups: ["Finance Team"]
|
||||
users: []
|
||||
allowed_permissions: [read]
|
||||
uri: /v1.0/process-instances/category_number_one:lanes/*
|
||||
|
|
|
@ -47,7 +47,7 @@ class SpecReferenceCache(SpiffworkflowBaseDBModel):
|
|||
file_name = db.Column(db.String(255))
|
||||
relative_path = db.Column(db.String(255))
|
||||
has_lanes = db.Column(db.Boolean())
|
||||
is_executable = db.Column(db.Boolean()) # either 'process' or 'decision'
|
||||
is_executable = db.Column(db.Boolean())
|
||||
is_primary = db.Column(db.Boolean())
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -232,11 +232,17 @@ def process_group_show(
|
|||
return make_response(jsonify(process_group), 200)
|
||||
|
||||
|
||||
def process_model_add(
|
||||
body: Dict[str, Union[str, bool, int]]
|
||||
def process_model_create(
|
||||
modified_process_group_id: str, body: Dict[str, Union[str, bool, int]]
|
||||
) -> flask.wrappers.Response:
|
||||
"""Add_process_model."""
|
||||
"""Process_model_create."""
|
||||
process_model_info = ProcessModelInfoSchema().load(body)
|
||||
if modified_process_group_id is None:
|
||||
raise ApiError(
|
||||
error_code="process_group_id_not_specified",
|
||||
message="Process Model could not be created when process_group_id path param is unspecified",
|
||||
status_code=400,
|
||||
)
|
||||
if process_model_info is None:
|
||||
raise ApiError(
|
||||
error_code="process_model_could_not_be_created",
|
||||
|
@ -1136,7 +1142,10 @@ def get_tasks(
|
|||
|
||||
|
||||
def process_instance_task_list(
|
||||
process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0
|
||||
modified_process_model_id: str,
|
||||
process_instance_id: int,
|
||||
all_tasks: bool = False,
|
||||
spiff_step: int = 0,
|
||||
) -> flask.wrappers.Response:
|
||||
"""Process_instance_task_list."""
|
||||
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
||||
|
@ -1199,6 +1208,7 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
|
|||
task = ProcessInstanceService.spiff_task_to_api_task(spiff_task)
|
||||
task.data = spiff_task.data
|
||||
task.process_model_display_name = process_model.display_name
|
||||
task.process_model_identifier = process_model.id
|
||||
process_model_with_form = process_model
|
||||
|
||||
if task.type == "User Task":
|
||||
|
|
|
@ -54,7 +54,8 @@ class FileSystemService:
|
|||
@staticmethod
|
||||
def process_group_path_for_spec(spec: ProcessModelInfo) -> str:
|
||||
"""Category_path_for_spec."""
|
||||
process_group_id, _ = os.path.split(spec.id)
|
||||
# os.path.split apparently returns 2 element tulple like: (first/path, last_item)
|
||||
process_group_id, _ = os.path.split(spec.id_for_file_path())
|
||||
return FileSystemService.process_group_path(process_group_id)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -701,9 +701,11 @@ class ProcessInstanceProcessor:
|
|||
"bpmn_file_full_path_from_bpmn_process_identifier: bpmn_process_identifier is unexpectedly None"
|
||||
)
|
||||
|
||||
spec_reference = SpecReferenceCache.query.filter_by(
|
||||
identifier=bpmn_process_identifier
|
||||
).first()
|
||||
spec_reference = (
|
||||
SpecReferenceCache.query.filter_by(identifier=bpmn_process_identifier)
|
||||
.filter_by(type="process")
|
||||
.first()
|
||||
)
|
||||
bpmn_file_full_path = None
|
||||
if spec_reference is None:
|
||||
bpmn_file_full_path = (
|
||||
|
|
|
@ -45,7 +45,9 @@ class SpecFileService(FileSystemService):
|
|||
) -> List[File]:
|
||||
"""Return all files associated with a workflow specification."""
|
||||
# path = SpecFileService.workflow_path(process_model_info)
|
||||
path = os.path.join(FileSystemService.root_path(), process_model_info.id)
|
||||
path = os.path.join(
|
||||
FileSystemService.root_path(), process_model_info.id_for_file_path()
|
||||
)
|
||||
files = SpecFileService._get_files(path, file_name)
|
||||
if extension_filter != "":
|
||||
files = list(
|
||||
|
@ -88,7 +90,7 @@ class SpecFileService(FileSystemService):
|
|||
"""
|
||||
references: list[SpecReference] = []
|
||||
full_file_path = SpecFileService.full_file_path(process_model_info, file.name)
|
||||
file_path = os.path.join(process_model_info.id, file.name)
|
||||
file_path = os.path.join(process_model_info.id_for_file_path(), file.name)
|
||||
parser = MyCustomParser()
|
||||
parser_type = None
|
||||
sub_parser = None
|
||||
|
@ -160,6 +162,8 @@ class SpecFileService(FileSystemService):
|
|||
(ref for ref in references if ref.is_primary and ref.is_executable), None
|
||||
)
|
||||
|
||||
SpecFileService.clear_caches_for_file(file_name, process_model_info)
|
||||
|
||||
for ref in references:
|
||||
# If no valid primary process is defined, default to the first process in the
|
||||
# updated file.
|
||||
|
@ -235,6 +239,16 @@ class SpecFileService(FileSystemService):
|
|||
SpecFileService.update_message_trigger_cache(ref)
|
||||
SpecFileService.update_correlation_cache(ref)
|
||||
|
||||
@staticmethod
|
||||
def clear_caches_for_file(
|
||||
file_name: str, process_model_info: ProcessModelInfo
|
||||
) -> None:
|
||||
"""Clear all caches related to a file."""
|
||||
db.session.query(SpecReferenceCache).filter(
|
||||
SpecReferenceCache.file_name == file_name
|
||||
).filter(SpecReferenceCache.process_model_id == process_model_info.id).delete()
|
||||
# fixme: likely the other caches should be cleared as well, but we don't have a clean way to do so yet.
|
||||
|
||||
@staticmethod
|
||||
def clear_caches() -> None:
|
||||
"""Clear_caches."""
|
||||
|
@ -254,6 +268,7 @@ class SpecFileService(FileSystemService):
|
|||
if process_id_lookup is None:
|
||||
process_id_lookup = SpecReferenceCache.from_spec_reference(ref)
|
||||
db.session.add(process_id_lookup)
|
||||
db.session.commit()
|
||||
else:
|
||||
if ref.relative_path != process_id_lookup.relative_path:
|
||||
full_bpmn_file_path = SpecFileService.full_path_from_relative_path(
|
||||
|
|
|
@ -136,6 +136,7 @@ class BaseTest:
|
|||
|
||||
# make sure we have a group
|
||||
process_group_id, _ = os.path.split(process_model_id)
|
||||
modified_process_group_id = process_group_id.replace("/", ":")
|
||||
process_group_path = f"{FileSystemService.root_path()}/{process_group_id}"
|
||||
if ProcessModelService().is_group(process_group_path):
|
||||
|
||||
|
@ -156,11 +157,12 @@ class BaseTest:
|
|||
user = self.find_or_create_user()
|
||||
|
||||
response = client.post(
|
||||
"/v1.0/process-models",
|
||||
f"/v1.0/process-models/{modified_process_group_id}",
|
||||
content_type="application/json",
|
||||
data=json.dumps(ProcessModelInfoSchema().dump(model)),
|
||||
headers=self.logged_in_headers(user),
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
return response
|
||||
|
||||
|
|
|
@ -143,7 +143,6 @@ class TestNestedGroups(BaseTest):
|
|||
response = client.get( # noqa: F841
|
||||
target_uri, headers=self.logged_in_headers(user)
|
||||
)
|
||||
print("test_nested_groups")
|
||||
|
||||
def test_add_nested_group(
|
||||
self,
|
||||
|
@ -153,10 +152,6 @@ class TestNestedGroups(BaseTest):
|
|||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test_add_nested_group."""
|
||||
# user = self.find_or_create_user()
|
||||
# self.add_permissions_to_user(
|
||||
# user, target_uri=target_uri, permission_names=["read", "create"]
|
||||
# )
|
||||
process_group_a = ProcessGroup(
|
||||
id="group_a",
|
||||
display_name="Group A",
|
||||
|
@ -194,16 +189,14 @@ class TestNestedGroups(BaseTest):
|
|||
data=json.dumps(ProcessGroupSchema().dump(process_group_c)),
|
||||
)
|
||||
|
||||
print("test_add_nested_group")
|
||||
|
||||
def test_process_model_add(
|
||||
def test_process_model_create(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test_process_model_add."""
|
||||
"""Test_process_model_create."""
|
||||
process_group_a = ProcessGroup(
|
||||
id="group_a",
|
||||
display_name="Group A",
|
||||
|
@ -242,7 +235,6 @@ class TestNestedGroups(BaseTest):
|
|||
content_type="application/json",
|
||||
data=json.dumps(ProcessModelInfoSchema().dump(process_model)),
|
||||
)
|
||||
print("test_process_model_add")
|
||||
|
||||
def test_process_group_show(
|
||||
self,
|
||||
|
|
|
@ -105,14 +105,14 @@ class TestProcessApi(BaseTest):
|
|||
assert response.json is not None
|
||||
assert response.json == expected_response_body
|
||||
|
||||
def test_process_model_add(
|
||||
def test_process_model_create(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test_add_new_process_model."""
|
||||
"""Test_process_model_create."""
|
||||
process_group_id = "test_process_group"
|
||||
process_group_display_name = "Test Process Group"
|
||||
# creates the group directory, and the json file
|
||||
|
|
|
@ -119,6 +119,43 @@ class TestSpecFileService(BaseTest):
|
|||
== self.call_activity_nested_relative_file_path
|
||||
)
|
||||
|
||||
def test_change_the_identifier_cleans_up_cache(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""When a BPMN processes identifier is changed in a file, the old id is removed from the cache."""
|
||||
old_identifier = "ye_old_identifier"
|
||||
process_id_lookup = SpecReferenceCache(
|
||||
identifier=old_identifier,
|
||||
relative_path=self.call_activity_nested_relative_file_path,
|
||||
file_name=self.bpmn_file_name,
|
||||
process_model_id=f"{self.process_group_id}/{self.process_model_id}",
|
||||
type="process",
|
||||
)
|
||||
db.session.add(process_id_lookup)
|
||||
db.session.commit()
|
||||
|
||||
self.create_group_and_model_with_bpmn(
|
||||
client=client,
|
||||
user=with_super_admin_user,
|
||||
process_group_id=self.process_group_id,
|
||||
process_model_id=self.process_model_id,
|
||||
bpmn_file_name=self.bpmn_file_name,
|
||||
bpmn_file_location=self.process_model_id,
|
||||
)
|
||||
|
||||
bpmn_process_id_lookups = SpecReferenceCache.query.all()
|
||||
assert len(bpmn_process_id_lookups) == 1
|
||||
assert bpmn_process_id_lookups[0].identifier != old_identifier
|
||||
assert bpmn_process_id_lookups[0].identifier == "Level1"
|
||||
assert (
|
||||
bpmn_process_id_lookups[0].relative_path
|
||||
== self.call_activity_nested_relative_file_path
|
||||
)
|
||||
|
||||
def test_load_reference_information(
|
||||
self,
|
||||
app: Flask,
|
||||
|
|
|
@ -36,6 +36,7 @@ module.exports = {
|
|||
],
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
|
|
|
@ -7,6 +7,9 @@ function error_handler() {
|
|||
trap 'error_handler ${LINENO} $?' ERR
|
||||
set -o errtrace -o errexit -o nounset -o pipefail
|
||||
|
||||
# see also: npx cypress run --env grep="can filter",grepFilterSpecs=true
|
||||
# https://github.com/cypress-io/cypress/tree/develop/npm/grep#pre-filter-specs-grepfilterspecs
|
||||
|
||||
command="${1:-}"
|
||||
if [[ -z "$command" ]]; then
|
||||
command=open
|
||||
|
|
|
@ -6,8 +6,9 @@ module.exports = defineConfig({
|
|||
chromeWebSecurity: false,
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:7001',
|
||||
setupNodeEvents(_on, _config) {
|
||||
// implement node event listeners here
|
||||
setupNodeEvents(_on, config) {
|
||||
require('@cypress/grep/src/plugin')(config);
|
||||
return config;
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -16,5 +16,9 @@
|
|||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
|
||||
registerCypressGrep();
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -96,6 +96,7 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/grep": "^3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.5",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"cypress": "^10.8.0",
|
||||
|
|
|
@ -24,7 +24,7 @@ export default function App() {
|
|||
[errorMessage]
|
||||
);
|
||||
|
||||
const ability = defineAbility((can: any) => {});
|
||||
const ability = defineAbility(() => {});
|
||||
|
||||
let errorTag = null;
|
||||
if (errorMessage) {
|
||||
|
@ -60,7 +60,7 @@ export default function App() {
|
|||
{errorTag}
|
||||
<ErrorBoundary>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePageRoutes />} />
|
||||
<Route path="/*" element={<HomePageRoutes />} />
|
||||
<Route path="/tasks/*" element={<HomePageRoutes />} />
|
||||
<Route path="/admin/*" element={<AdminRoutes />} />
|
||||
</Routes>
|
||||
|
|
|
@ -74,10 +74,7 @@ export default function ProcessModelForm({
|
|||
if (hasErrors) {
|
||||
return;
|
||||
}
|
||||
let path = `/process-models`;
|
||||
if (mode === 'edit') {
|
||||
path = `/process-models/${modifiedProcessModelPath}`;
|
||||
}
|
||||
const path = `/process-models/${modifiedProcessModelPath}`;
|
||||
let httpMethod = 'POST';
|
||||
if (mode === 'edit') {
|
||||
httpMethod = 'PUT';
|
||||
|
|
|
@ -52,10 +52,14 @@ import TouchModule from 'diagram-js/lib/navigation/touch';
|
|||
// @ts-expect-error TS(7016) FIXME
|
||||
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
|
||||
|
||||
import { Can } from '@casl/react';
|
||||
import HttpService from '../services/HttpService';
|
||||
|
||||
import ButtonWithConfirmation from './ButtonWithConfirmation';
|
||||
import { makeid } from '../helpers';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { PermissionsToCheck } from '../interfaces';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
|
||||
type OwnProps = {
|
||||
processModelId: string;
|
||||
|
@ -107,6 +111,13 @@ export default function ReactDiagramEditor({
|
|||
|
||||
const alreadyImportedXmlRef = useRef(false);
|
||||
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.processModelShowPath]: ['PUT'],
|
||||
[targetUris.processModelFileShowPath]: ['POST', 'GET', 'PUT', 'DELETE'],
|
||||
};
|
||||
const { ability } = usePermissionFetcher(permissionRequestData);
|
||||
|
||||
useEffect(() => {
|
||||
if (diagramModelerState) {
|
||||
return;
|
||||
|
@ -517,20 +528,40 @@ export default function ReactDiagramEditor({
|
|||
if (diagramType !== 'readonly') {
|
||||
return (
|
||||
<>
|
||||
<Button onClick={handleSave} variant="danger">
|
||||
Save
|
||||
</Button>
|
||||
{fileName && (
|
||||
<ButtonWithConfirmation
|
||||
description={`Delete file ${fileName}?`}
|
||||
onConfirmation={handleDelete}
|
||||
buttonLabel="Delete"
|
||||
/>
|
||||
)}
|
||||
{onSetPrimaryFile && (
|
||||
<Button onClick={handleSetPrimaryFile}>Set as primary file</Button>
|
||||
)}
|
||||
<Button onClick={downloadXmlFile}>Download xml</Button>
|
||||
<Can
|
||||
I="PUT"
|
||||
a={targetUris.processModelFileShowPath}
|
||||
ability={ability}
|
||||
>
|
||||
<Button onClick={handleSave}>Save</Button>
|
||||
</Can>
|
||||
<Can
|
||||
I="DELETE"
|
||||
a={targetUris.processModelFileShowPath}
|
||||
ability={ability}
|
||||
>
|
||||
{fileName && (
|
||||
<ButtonWithConfirmation
|
||||
description={`Delete file ${fileName}?`}
|
||||
onConfirmation={handleDelete}
|
||||
buttonLabel="Delete"
|
||||
/>
|
||||
)}
|
||||
</Can>
|
||||
<Can I="PUT" a={targetUris.processModelShowPath} ability={ability}>
|
||||
{onSetPrimaryFile && (
|
||||
<Button onClick={handleSetPrimaryFile}>
|
||||
Set as primary file
|
||||
</Button>
|
||||
)}
|
||||
</Can>
|
||||
<Can
|
||||
I="GET"
|
||||
a={targetUris.processModelFileShowPath}
|
||||
ability={ability}
|
||||
>
|
||||
<Button onClick={downloadXmlFile}>Download xml</Button>
|
||||
</Can>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createContext } from 'react';
|
||||
import { AbilityBuilder, Ability } from '@casl/ability';
|
||||
import { Ability } from '@casl/ability';
|
||||
import { createContextualCan } from '@casl/react';
|
||||
|
||||
export const AbilityContext = createContext(new Ability());
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// We may need to update usage of Ability when we update.
|
||||
// They say they are going to rename PureAbility to Ability and remove the old class.
|
||||
import { AbilityBuilder, Ability } from '@casl/ability';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { AbilityContext } from '../contexts/Can';
|
||||
|
@ -11,28 +13,34 @@ export const usePermissionFetcher = (
|
|||
|
||||
useEffect(() => {
|
||||
const processPermissionResult = (result: PermissionCheckResponseBody) => {
|
||||
const oldRules = ability.rules;
|
||||
const { can, cannot, rules } = new AbilityBuilder(Ability);
|
||||
for (const [url, permissionVerbResults] of Object.entries(
|
||||
result.results
|
||||
)) {
|
||||
for (const [permissionVerb, hasPermission] of Object.entries(
|
||||
permissionVerbResults
|
||||
)) {
|
||||
Object.keys(result.results).forEach((url: string) => {
|
||||
const permissionVerbResults = result.results[url];
|
||||
Object.keys(permissionVerbResults).forEach((permissionVerb: string) => {
|
||||
const hasPermission = permissionVerbResults[permissionVerb];
|
||||
if (hasPermission) {
|
||||
can(permissionVerb, url);
|
||||
} else {
|
||||
cannot(permissionVerb, url);
|
||||
}
|
||||
});
|
||||
});
|
||||
oldRules.forEach((oldRule: any) => {
|
||||
if (oldRule.inverted) {
|
||||
cannot(oldRule.action, oldRule.subject);
|
||||
} else {
|
||||
can(oldRule.action, oldRule.subject);
|
||||
}
|
||||
}
|
||||
});
|
||||
ability.update(rules);
|
||||
};
|
||||
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/permissions-check`,
|
||||
httpMethod: 'POST',
|
||||
successCallback: processPermissionResult,
|
||||
postBody: { requests_to_check: permissionsToCheck },
|
||||
// failureCallback: setErrorMessage,
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { useParams } from 'react-router-dom';
|
||||
|
||||
export const useUriListForPermissions = () => {
|
||||
const params = useParams();
|
||||
const targetUris = {
|
||||
messageInstanceListPath: '/v1.0/messages',
|
||||
processGroupListPath: '/v1.0/process-groups',
|
||||
processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`,
|
||||
processInstanceActionPath: `/v1.0/process-models/${params.process_model_id}/process-instances`,
|
||||
processInstanceListPath: '/v1.0/process-instances',
|
||||
processModelCreatePath: `/v1.0/process-models/${params.process_group_id}`,
|
||||
processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`,
|
||||
processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`,
|
||||
processModelShowPath: `/v1.0/process-models/${params.process_model_id}`,
|
||||
};
|
||||
|
||||
return { targetUris };
|
||||
};
|
|
@ -74,12 +74,12 @@ export interface CarbonComboBoxSelection {
|
|||
export interface PermissionsToCheck {
|
||||
[key: string]: string[];
|
||||
}
|
||||
export interface PermissionCheckResponseBody {
|
||||
results: PermissionCheckResult;
|
||||
export interface PermissionVerbResults {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
export interface PermissionCheckResult {
|
||||
[key: string]: PermissionVerbResults;
|
||||
}
|
||||
export interface PermissionVerbResults {
|
||||
[key: string]: boolean;
|
||||
export interface PermissionCheckResponseBody {
|
||||
results: PermissionCheckResult;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
// ClickableTile,
|
||||
// @ts-ignore
|
||||
} from '@carbon/react';
|
||||
import { Can } from '@casl/react';
|
||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||
import PaginationForTable from '../components/PaginationForTable';
|
||||
import HttpService from '../services/HttpService';
|
||||
|
@ -17,8 +18,14 @@ import {
|
|||
getPageInfoFromSearchParams,
|
||||
modifyProcessModelPath,
|
||||
} from '../helpers';
|
||||
import { CarbonComboBoxSelection, ProcessGroup } from '../interfaces';
|
||||
import {
|
||||
CarbonComboBoxSelection,
|
||||
PermissionsToCheck,
|
||||
ProcessGroup,
|
||||
} from '../interfaces';
|
||||
import ProcessModelSearch from '../components/ProcessModelSearch';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
|
||||
// Example process group json
|
||||
// {'process_group_id': 'sure', 'display_name': 'Test Workflows', 'id': 'test_process_group'}
|
||||
|
@ -32,6 +39,12 @@ export default function ProcessGroupList() {
|
|||
[]
|
||||
);
|
||||
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.processGroupListPath]: ['POST'],
|
||||
};
|
||||
const { ability } = usePermissionFetcher(permissionRequestData);
|
||||
|
||||
useEffect(() => {
|
||||
const setProcessGroupsFromResult = (result: any) => {
|
||||
setProcessGroups(result.results);
|
||||
|
@ -84,17 +97,6 @@ export default function ProcessGroupList() {
|
|||
<tbody>{rows}</tbody>
|
||||
</Table>
|
||||
);
|
||||
// const rows = processGroups.map((row: ProcessGroup) => {
|
||||
// return (
|
||||
// <span>
|
||||
// <ClickableTile href={`/admin/process-groups/${row.id}`}>
|
||||
// {row.display_name}
|
||||
// </ClickableTile>
|
||||
// </span>
|
||||
// );
|
||||
// });
|
||||
//
|
||||
// return <div style={{ width: '400px' }}>{rows}</div>;
|
||||
};
|
||||
|
||||
const processGroupsDisplayArea = () => {
|
||||
|
@ -138,11 +140,13 @@ export default function ProcessGroupList() {
|
|||
return (
|
||||
<>
|
||||
<ProcessBreadcrumb hotCrumbs={[['Process Groups']]} />
|
||||
<Button kind="secondary" href="/admin/process-groups/new">
|
||||
Add a process group
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
<Can I="POST" a={targetUris.processGroupListPath} ability={ability}>
|
||||
<Button kind="secondary" href="/admin/process-groups/new">
|
||||
Add a process group
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
</Can>
|
||||
{processModelSearchArea()}
|
||||
<br />
|
||||
{processGroupsDisplayArea()}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
|||
import { Link, useSearchParams, useParams } from 'react-router-dom';
|
||||
// @ts-ignore
|
||||
import { Button, Table, Stack } from '@carbon/react';
|
||||
import { Can } from '@casl/react';
|
||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||
import PaginationForTable from '../components/PaginationForTable';
|
||||
import HttpService from '../services/HttpService';
|
||||
|
@ -10,7 +11,14 @@ import {
|
|||
modifyProcessModelPath,
|
||||
unModifyProcessModelPath,
|
||||
} from '../helpers';
|
||||
import { PaginationObject, ProcessGroup, ProcessModel } from '../interfaces';
|
||||
import {
|
||||
PaginationObject,
|
||||
PermissionsToCheck,
|
||||
ProcessGroup,
|
||||
ProcessModel,
|
||||
} from '../interfaces';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
|
||||
export default function ProcessGroupShow() {
|
||||
const params = useParams();
|
||||
|
@ -24,6 +32,14 @@ export default function ProcessGroupShow() {
|
|||
const [groupPagination, setGroupPagination] =
|
||||
useState<PaginationObject | null>(null);
|
||||
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.processGroupListPath]: ['POST'],
|
||||
[targetUris.processGroupShowPath]: ['PUT'],
|
||||
[targetUris.processModelCreatePath]: ['POST'],
|
||||
};
|
||||
const { ability } = usePermissionFetcher(permissionRequestData);
|
||||
|
||||
useEffect(() => {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
||||
|
||||
|
@ -143,23 +159,31 @@ export default function ProcessGroupShow() {
|
|||
<h1>Process Group: {processGroup.display_name}</h1>
|
||||
<ul>
|
||||
<Stack orientation="horizontal" gap={3}>
|
||||
<Button
|
||||
kind="secondary"
|
||||
href={`/admin/process-groups/new?parentGroupId=${processGroup.id}`}
|
||||
<Can I="POST" a={targetUris.processGroupListPath} ability={ability}>
|
||||
<Button
|
||||
href={`/admin/process-groups/new?parentGroupId=${processGroup.id}`}
|
||||
>
|
||||
Add a process group
|
||||
</Button>
|
||||
</Can>
|
||||
<Can
|
||||
I="POST"
|
||||
a={targetUris.processModelCreatePath}
|
||||
ability={ability}
|
||||
>
|
||||
Add a process group
|
||||
</Button>
|
||||
<Button
|
||||
href={`/admin/process-models/${modifiedProcessGroupId}/new`}
|
||||
>
|
||||
Add a process model
|
||||
</Button>
|
||||
<Button
|
||||
href={`/admin/process-groups/${modifiedProcessGroupId}/edit`}
|
||||
variant="secondary"
|
||||
>
|
||||
Edit process group
|
||||
</Button>
|
||||
<Button
|
||||
href={`/admin/process-models/${modifiedProcessGroupId}/new`}
|
||||
>
|
||||
Add a process model
|
||||
</Button>
|
||||
</Can>
|
||||
<Can I="PUT" a={targetUris.processGroupShowPath} ability={ability}>
|
||||
<Button
|
||||
href={`/admin/process-groups/${modifiedProcessGroupId}/edit`}
|
||||
>
|
||||
Edit process group
|
||||
</Button>
|
||||
</Can>
|
||||
</Stack>
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
Stack,
|
||||
// @ts-ignore
|
||||
} from '@carbon/react';
|
||||
import { Can } from '@casl/react';
|
||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||
import HttpService from '../services/HttpService';
|
||||
import ReactDiagramEditor from '../components/ReactDiagramEditor';
|
||||
|
@ -32,6 +33,9 @@ import {
|
|||
} from '../helpers';
|
||||
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
||||
import ErrorContext from '../contexts/ErrorContext';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import { PermissionsToCheck } from '../interfaces';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
|
||||
export default function ProcessInstanceShow() {
|
||||
const navigate = useNavigate();
|
||||
|
@ -50,6 +54,12 @@ export default function ProcessInstanceShow() {
|
|||
);
|
||||
const modifiedProcessModelId = params.process_model_id;
|
||||
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.messageInstanceListPath]: ['GET'],
|
||||
};
|
||||
const { ability } = usePermissionFetcher(permissionRequestData);
|
||||
|
||||
const navigateToProcessInstances = (_result: any) => {
|
||||
navigate(
|
||||
`/admin/process-instances?process_model_identifier=${unModifiedProcessModelId}`
|
||||
|
@ -63,12 +73,12 @@ export default function ProcessInstanceShow() {
|
|||
});
|
||||
if (typeof params.spiff_step === 'undefined')
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true`,
|
||||
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}/tasks?all_tasks=true`,
|
||||
successCallback: setTasks,
|
||||
});
|
||||
else
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true&spiff_step=${params.spiff_step}`,
|
||||
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}/tasks?all_tasks=true&spiff_step=${params.spiff_step}`,
|
||||
successCallback: setTasks,
|
||||
});
|
||||
}, [params, modifiedProcessModelId]);
|
||||
|
@ -245,14 +255,20 @@ export default function ProcessInstanceShow() {
|
|||
>
|
||||
Logs
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="button-white-background"
|
||||
data-qa="process-instance-message-instance-list-link"
|
||||
href={`/admin/messages?process_model_id=${params.process_model_id}&process_instance_id=${params.process_instance_id}`}
|
||||
<Can
|
||||
I="GET"
|
||||
a={targetUris.messageInstanceListPath}
|
||||
ability={ability}
|
||||
>
|
||||
Messages
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="button-white-background"
|
||||
data-qa="process-instance-message-instance-list-link"
|
||||
href={`/admin/messages?process_model_id=${params.process_model_id}&process_instance_id=${params.process_instance_id}`}
|
||||
>
|
||||
Messages
|
||||
</Button>
|
||||
</Can>
|
||||
</ButtonSet>
|
||||
</Column>
|
||||
</Grid>
|
||||
|
|
|
@ -40,7 +40,8 @@ import {
|
|||
} from '../interfaces';
|
||||
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
||||
import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
|
||||
import { usePermissionFetcher } from '../components/PermissionService';
|
||||
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
|
||||
const storeRecentProcessModelInLocalStorage = (
|
||||
processModelForStorage: ProcessModel
|
||||
|
@ -103,16 +104,12 @@ export default function ProcessModelShow() {
|
|||
useState<boolean>(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const targetUris = {
|
||||
processModelPath: `/process-models/${params.process_model_id}`,
|
||||
processInstancesPath: `/process-instances`,
|
||||
};
|
||||
const { targetUris } = useUriListForPermissions();
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[`/v1.0${targetUris.processModelPath}`]: ['GET', 'PUT'],
|
||||
[`/v1.0${targetUris.processInstancesPath}`]: ['GET'],
|
||||
[`/v1.0${targetUris.processModelPath}${targetUris.processInstancesPath}`]: [
|
||||
'POST',
|
||||
],
|
||||
[targetUris.processModelShowPath]: ['PUT'],
|
||||
[targetUris.processInstanceListPath]: ['GET'],
|
||||
[targetUris.processInstanceActionPath]: ['POST'],
|
||||
[targetUris.processModelFileCreatePath]: ['POST', 'GET', 'DELETE'],
|
||||
};
|
||||
const { ability } = usePermissionFetcher(permissionRequestData);
|
||||
|
||||
|
@ -267,50 +264,62 @@ export default function ProcessModelShow() {
|
|||
) => {
|
||||
const elements = [];
|
||||
elements.push(
|
||||
<Button
|
||||
kind="ghost"
|
||||
renderIcon={Edit}
|
||||
iconDescription="Edit File"
|
||||
hasIconOnly
|
||||
size="lg"
|
||||
data-qa={`edit-file-${processModelFile.name.replace('.', '-')}`}
|
||||
onClick={() => navigateToFileEdit(processModelFile)}
|
||||
/>
|
||||
<Can I="GET" a={targetUris.processModelFileCreatePath} ability={ability}>
|
||||
<Button
|
||||
kind="ghost"
|
||||
renderIcon={Edit}
|
||||
iconDescription="Edit File"
|
||||
hasIconOnly
|
||||
size="lg"
|
||||
data-qa={`edit-file-${processModelFile.name.replace('.', '-')}`}
|
||||
onClick={() => navigateToFileEdit(processModelFile)}
|
||||
/>
|
||||
</Can>
|
||||
);
|
||||
elements.push(
|
||||
<Button
|
||||
kind="ghost"
|
||||
renderIcon={Download}
|
||||
iconDescription="Download File"
|
||||
hasIconOnly
|
||||
size="lg"
|
||||
onClick={() => downloadFile(processModelFile.name)}
|
||||
/>
|
||||
<Can I="GET" a={targetUris.processModelFileCreatePath} ability={ability}>
|
||||
<Button
|
||||
kind="ghost"
|
||||
renderIcon={Download}
|
||||
iconDescription="Download File"
|
||||
hasIconOnly
|
||||
size="lg"
|
||||
onClick={() => downloadFile(processModelFile.name)}
|
||||
/>
|
||||
</Can>
|
||||
);
|
||||
|
||||
elements.push(
|
||||
<ButtonWithConfirmation
|
||||
kind="ghost"
|
||||
renderIcon={TrashCan}
|
||||
iconDescription="Delete File"
|
||||
hasIconOnly
|
||||
description={`Delete file: ${processModelFile.name}`}
|
||||
onConfirmation={() => {
|
||||
onDeleteFile(processModelFile.name);
|
||||
}}
|
||||
confirmButtonLabel="Delete"
|
||||
/>
|
||||
<Can
|
||||
I="DELETE"
|
||||
a={targetUris.processModelFileCreatePath}
|
||||
ability={ability}
|
||||
>
|
||||
<ButtonWithConfirmation
|
||||
kind="ghost"
|
||||
renderIcon={TrashCan}
|
||||
iconDescription="Delete File"
|
||||
hasIconOnly
|
||||
description={`Delete file: ${processModelFile.name}`}
|
||||
onConfirmation={() => {
|
||||
onDeleteFile(processModelFile.name);
|
||||
}}
|
||||
confirmButtonLabel="Delete"
|
||||
/>
|
||||
</Can>
|
||||
);
|
||||
if (processModelFile.name.match(/\.bpmn$/) && !isPrimaryBpmnFile) {
|
||||
elements.push(
|
||||
<Button
|
||||
kind="ghost"
|
||||
renderIcon={Favorite}
|
||||
iconDescription="Set As Primary File"
|
||||
hasIconOnly
|
||||
size="lg"
|
||||
onClick={() => onSetPrimaryFile(processModelFile.name)}
|
||||
/>
|
||||
<Can I="PUT" a={targetUris.processModelShowPath} ability={ability}>
|
||||
<Button
|
||||
kind="ghost"
|
||||
renderIcon={Favorite}
|
||||
iconDescription="Set As Primary File"
|
||||
hasIconOnly
|
||||
size="lg"
|
||||
onClick={() => onSetPrimaryFile(processModelFile.name)}
|
||||
/>
|
||||
</Can>
|
||||
);
|
||||
}
|
||||
return elements;
|
||||
|
@ -341,7 +350,11 @@ export default function ProcessModelShow() {
|
|||
let fileLink = null;
|
||||
const fileUrl = profileModelFileEditUrl(processModelFile);
|
||||
if (fileUrl) {
|
||||
fileLink = <Link to={fileUrl}>{processModelFile.name}</Link>;
|
||||
if (ability.can('GET', targetUris.processModelFileCreatePath)) {
|
||||
fileLink = <Link to={fileUrl}>{processModelFile.name}</Link>;
|
||||
} else {
|
||||
fileLink = <span>{processModelFile.name}</span>;
|
||||
}
|
||||
}
|
||||
constructedTag = (
|
||||
<TableRow key={processModelFile.name}>
|
||||
|
@ -446,47 +459,53 @@ export default function ProcessModelShow() {
|
|||
</Stack>
|
||||
}
|
||||
>
|
||||
<ButtonSet>
|
||||
<Button
|
||||
renderIcon={Upload}
|
||||
data-qa="upload-file-button"
|
||||
onClick={() => setShowFileUploadModal(true)}
|
||||
size="sm"
|
||||
kind=""
|
||||
className="button-white-background"
|
||||
>
|
||||
Upload File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=bpmn`}
|
||||
size="sm"
|
||||
>
|
||||
New BPMN File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=dmn`}
|
||||
size="sm"
|
||||
>
|
||||
New DMN File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=json`}
|
||||
size="sm"
|
||||
>
|
||||
New JSON File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=md`}
|
||||
size="sm"
|
||||
>
|
||||
New Markdown File
|
||||
</Button>
|
||||
</ButtonSet>
|
||||
<br />
|
||||
<Can
|
||||
I="POST"
|
||||
a={targetUris.processModelFileCreatePath}
|
||||
ability={ability}
|
||||
>
|
||||
<ButtonSet>
|
||||
<Button
|
||||
renderIcon={Upload}
|
||||
data-qa="upload-file-button"
|
||||
onClick={() => setShowFileUploadModal(true)}
|
||||
size="sm"
|
||||
kind=""
|
||||
className="button-white-background"
|
||||
>
|
||||
Upload File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=bpmn`}
|
||||
size="sm"
|
||||
>
|
||||
New BPMN File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=dmn`}
|
||||
size="sm"
|
||||
>
|
||||
New DMN File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=json`}
|
||||
size="sm"
|
||||
>
|
||||
New JSON File
|
||||
</Button>
|
||||
<Button
|
||||
renderIcon={Add}
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=md`}
|
||||
size="sm"
|
||||
>
|
||||
New Markdown File
|
||||
</Button>
|
||||
</ButtonSet>
|
||||
<br />
|
||||
</Can>
|
||||
{processModelFileList()}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
@ -513,18 +532,14 @@ export default function ProcessModelShow() {
|
|||
<Stack orientation="horizontal" gap={3}>
|
||||
<Can
|
||||
I="POST"
|
||||
a={`/v1.0${targetUris.processModelPath}${targetUris.processInstancesPath}`}
|
||||
a={targetUris.processInstanceActionPath}
|
||||
ability={ability}
|
||||
>
|
||||
<Button onClick={processInstanceCreateAndRun} variant="primary">
|
||||
Run
|
||||
</Button>
|
||||
</Can>
|
||||
<Can
|
||||
I="PUT"
|
||||
a={`/v1.0${targetUris.processModelPath}`}
|
||||
ability={ability}
|
||||
>
|
||||
<Can I="PUT" a={targetUris.processModelShowPath} ability={ability}>
|
||||
<Button
|
||||
href={`/admin/process-models/${modifiedProcessModelId}/edit`}
|
||||
variant="secondary"
|
||||
|
@ -537,11 +552,7 @@ export default function ProcessModelShow() {
|
|||
<br />
|
||||
{processInstanceRunResultTag()}
|
||||
<br />
|
||||
<Can
|
||||
I="GET"
|
||||
a={`/v1.0${targetUris.processInstancesPath}`}
|
||||
ability={ability}
|
||||
>
|
||||
<Can I="GET" a={targetUris.processInstanceListPath} ability={ability}>
|
||||
<ProcessInstanceListTable
|
||||
filtersEnabled={false}
|
||||
processModelFullIdentifier={processModel.id}
|
||||
|
|
|
@ -8,6 +8,7 @@ import ReactMarkdown from 'react-markdown';
|
|||
import remarkGfm from 'remark-gfm';
|
||||
import HttpService from '../services/HttpService';
|
||||
import ErrorContext from '../contexts/ErrorContext';
|
||||
import { modifyProcessModelPath } from '../helpers';
|
||||
|
||||
export default function TaskShow() {
|
||||
const [task, setTask] = useState(null);
|
||||
|
@ -18,16 +19,22 @@ export default function TaskShow() {
|
|||
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||
|
||||
useEffect(() => {
|
||||
const processResult = (result: any) => {
|
||||
setTask(result);
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instances/${modifyProcessModelPath(
|
||||
result.process_model_identifier
|
||||
)}/${params.process_instance_id}/tasks`,
|
||||
successCallback: setUserTasks,
|
||||
});
|
||||
};
|
||||
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
||||
successCallback: setTask,
|
||||
successCallback: processResult,
|
||||
// This causes the page to continuously reload
|
||||
// failureCallback: setErrorMessage,
|
||||
});
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-instance/${params.process_instance_id}/tasks`,
|
||||
successCallback: setUserTasks,
|
||||
});
|
||||
}, [params]);
|
||||
|
||||
const processSubmitResult = (result: any) => {
|
||||
|
|
Loading…
Reference in New Issue