fix conflicts again

This commit is contained in:
Elizabeth Esswein 2022-12-16 16:27:41 -05:00
commit 8d8f766d45
23 changed files with 643 additions and 569 deletions

View File

@ -170,15 +170,17 @@ def set_user_sentry_context() -> None:
def handle_exception(exception: Exception) -> flask.wrappers.Response: def handle_exception(exception: Exception) -> flask.wrappers.Response:
"""Handles unexpected exceptions.""" """Handles unexpected exceptions."""
set_user_sentry_context() set_user_sentry_context()
id = capture_exception(exception)
organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG")
project_slug = current_app.config.get("SENTRY_PROJECT_SLUG")
sentry_link = None sentry_link = None
if organization_slug and project_slug: if not isinstance(exception, ApiError) or exception.error_code != "invalid_token":
sentry_link = ( id = capture_exception(exception)
f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}"
) organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG")
project_slug = current_app.config.get("SENTRY_PROJECT_SLUG")
if organization_slug and project_slug:
sentry_link = (
f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}"
)
# !!!NOTE!!!: do this after sentry stuff since calling logger.exception # !!!NOTE!!!: do this after sentry stuff since calling logger.exception
# seems to break the sentry sdk context where we no longer get back # seems to break the sentry sdk context where we no longer get back

View File

@ -654,7 +654,7 @@ werkzeug = "*"
type = "git" type = "git"
url = "https://github.com/sartography/flask-bpmn" url = "https://github.com/sartography/flask-bpmn"
reference = "main" reference = "main"
resolved_reference = "860f2387bebdaa9220e9fbf6f8fa7f74e805d0d4" resolved_reference = "0f2d249d0e799bec912d46132e9ef9754fdacbd7"
[[package]] [[package]]
name = "Flask-Cors" name = "Flask-Cors"
@ -1851,7 +1851,7 @@ lxml = "*"
type = "git" type = "git"
url = "https://github.com/sartography/SpiffWorkflow" url = "https://github.com/sartography/SpiffWorkflow"
reference = "main" reference = "main"
resolved_reference = "ffb1686757f944065580dd2db8def73d6c1f0134" resolved_reference = "841bd63017bb1d92858456393f144b4e5b23c994"
[[package]] [[package]]
name = "SQLAlchemy" name = "SQLAlchemy"
@ -2563,7 +2563,6 @@ greenlet = [
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"}, {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"},
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"}, {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"},
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"}, {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"},
{file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0459d94f73265744fee4c2d5ec44c6f34aa8a31017e6e9de770f7bcf29710be9"},
{file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"}, {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"},
{file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"}, {file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"},
{file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"}, {file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"},
@ -2572,7 +2571,6 @@ greenlet = [
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"}, {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"},
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"}, {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"},
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"}, {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"},
{file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d38ffd0e81ba8ef347d2be0772e899c289b59ff150ebbbbe05dc61b1246eb4e0"},
{file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"}, {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"},
{file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"}, {file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"},
{file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"}, {file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"},
@ -2581,7 +2579,6 @@ greenlet = [
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"}, {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"},
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"}, {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"},
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"}, {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"},
{file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:662e8f7cad915ba75d8017b3e601afc01ef20deeeabf281bd00369de196d7726"},
{file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"}, {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"},
{file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"}, {file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"},
{file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"}, {file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"},
@ -2880,7 +2877,10 @@ orjson = [
{file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"}, {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"},
{file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"}, {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"},
{file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"}, {file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"},
{file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"},
{file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"},
{file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"}, {file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"},
{file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"},
{file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"},
{file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"},
{file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"}, {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"},
@ -2989,18 +2989,7 @@ psycopg2 = [
{file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"}, {file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"},
] ]
pyasn1 = [ 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-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"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
] ]
pycodestyle = [ pycodestyle = [

View File

@ -595,6 +595,12 @@ paths:
description: Specifies the identifier of a report to use, if any description: Specifies the identifier of a report to use, if any
schema: schema:
type: integer type: integer
- name: group_identifier
in: query
required: false
description: The identifier of the group to get the process instances for
schema:
type: string
get: get:
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_list operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_list
summary: Returns a list of process instances for a given process model summary: Returns a list of process instances for a given process model
@ -673,6 +679,53 @@ paths:
schema: schema:
$ref: "#/components/schemas/Workflow" $ref: "#/components/schemas/Workflow"
/process-instances/{modified_process_model_identifier}/{process_instance_id}/task-info:
parameters:
- name: modified_process_model_identifier
in: path
required: true
description: The unique id of an existing process model
schema:
type: string
- name: process_instance_id
in: path
required: true
description: The unique id of an existing process instance.
schema:
type: integer
- name: process_identifier
in: query
required: false
description: The identifier of the process to use for the diagram. Useful for displaying the diagram for a call activity.
schema:
type: string
- name: all_tasks
in: query
required: false
description: If true, this wil return all tasks associated with the process instance and not just user tasks.
schema:
type: boolean
- name: spiff_step
in: query
required: false
description: If set will return the tasks as they were during a specific step of execution.
schema:
type: integer
get:
tags:
- Process Instances
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_task_list_without_task_data
summary: returns the list of all user tasks associated with process instance without the task data
responses:
"200":
description: list of tasks
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Task"
/process-instances/{modified_process_model_identifier}/{process_instance_id}: /process-instances/{modified_process_model_identifier}/{process_instance_id}:
parameters: parameters:
- name: modified_process_model_identifier - name: modified_process_model_identifier
@ -1103,6 +1156,12 @@ paths:
/tasks/for-my-groups: /tasks/for-my-groups:
parameters: parameters:
- name: group_identifier
in: query
required: false
description: The identifier of the group to get the tasks for
schema:
type: string
- name: page - name: page
in: query in: query
required: false required: false
@ -1130,6 +1189,22 @@ paths:
items: items:
$ref: "#/components/schemas/Task" $ref: "#/components/schemas/Task"
/user-groups/for-current-user:
get:
tags:
- Process Instances
operationId: spiffworkflow_backend.routes.process_api_blueprint.user_group_list_for_current_user
summary: Group identifiers for current logged in user
responses:
"200":
description: list of user groups
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Task"
/task-data/{modified_process_model_identifier}/{process_instance_id}: /task-data/{modified_process_model_identifier}/{process_instance_id}:
parameters: parameters:
- name: modified_process_model_identifier - name: modified_process_model_identifier
@ -1159,8 +1234,8 @@ paths:
get: get:
tags: tags:
- Process Instances - Process Instances
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_task_list operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_task_list_with_task_data
summary: returns the list of all user tasks associated with process instance summary: returns the list of all user tasks associated with process instance with the task data
responses: responses:
"200": "200":
description: list of tasks description: list of tasks

View File

@ -9,5 +9,5 @@ permissions:
admin: admin:
groups: [admin, common-user] groups: [admin, common-user]
users: [] users: []
allowed_permissions: [create, read, update, delete, list, instantiate] allowed_permissions: [create, read, update, delete]
uri: /* uri: /*

View File

@ -20,7 +20,6 @@ groups:
jarrad, jarrad,
elizabeth, elizabeth,
jon, jon,
natalia,
] ]
Finance Team: Finance Team:
@ -35,7 +34,6 @@ groups:
jarrad, jarrad,
elizabeth, elizabeth,
jon, jon,
natalia,
sasha, sasha,
fin, fin,
fin1, fin1,
@ -48,6 +46,7 @@ groups:
fin, fin,
fin1, fin1,
harmeet, harmeet,
jason,
sasha, sasha,
manuchehr, manuchehr,
lead, lead,
@ -67,6 +66,9 @@ groups:
j, j,
] ]
test:
users: [natalia]
permissions: permissions:
admin: admin:
groups: [admin] groups: [admin]
@ -95,6 +97,11 @@ permissions:
users: [] users: []
allowed_permissions: [read] allowed_permissions: [read]
uri: /v1.0/service-tasks uri: /v1.0/service-tasks
user-groups-for-current-user:
groups: [everybody]
users: []
allowed_permissions: [read]
uri: /v1.0/user-groups/for-current-user
# read all for everybody # read all for everybody
@ -124,12 +131,6 @@ permissions:
allowed_permissions: [read] allowed_permissions: [read]
uri: /v1.0/processes uri: /v1.0/processes
task-data-read:
groups: [demo]
users: []
allowed_permissions: [read]
uri: /v1.0/task-data/*
manage-procurement-admin: manage-procurement-admin:
groups: ["Project Lead"] groups: ["Project Lead"]
@ -168,44 +169,30 @@ permissions:
allowed_permissions: [create, read, update, delete] allowed_permissions: [create, read, update, delete]
uri: /v1.0/process-groups/manage-procurement:procurement:* uri: /v1.0/process-groups/manage-procurement:procurement:*
manage-revenue-streams-instantiate:
groups: ["core-contributor", "demo"]
users: []
allowed_permissions: [create]
uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
manage-revenue-streams-instances: manage-revenue-streams-instances:
groups: ["core-contributor", "demo"] groups: ["core-contributor", "demo"]
users: [] users: []
allowed_permissions: [create, read] allowed_permissions: [create, read]
uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/*
manage-procurement-invoice-instantiate:
groups: ["core-contributor", "demo"]
users: []
allowed_permissions: [create]
uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:*
manage-procurement-invoice-instances: manage-procurement-invoice-instances:
groups: ["core-contributor", "demo"] groups: ["core-contributor", "demo"]
users: [] users: []
allowed_permissions: [create, read] allowed_permissions: [create, read]
uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:* uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:*
manage-procurement-instantiate:
groups: ["core-contributor", "demo"]
users: []
allowed_permissions: [create]
uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:*
manage-procurement-instances: manage-procurement-instances:
groups: ["core-contributor", "demo"] groups: ["core-contributor", "demo"]
users: [] users: []
allowed_permissions: [create, read] allowed_permissions: [create, read]
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
core1-admin-models-instantiate: create-test-instances:
groups: ["core-contributor", "Finance Team"] groups: ["test"]
users: [] users: []
allowed_permissions: [create] allowed_permissions: [create, read]
uri: /v1.0/process-models/misc:category_number_one:process-model-with-form/process-instances uri: /v1.0/process-instances/misc:test:*
core1-admin-instances: core1-admin-instances:
groups: ["core-contributor", "Finance Team"] groups: ["core-contributor", "Finance Team"]
users: [] users: []

View File

@ -80,6 +80,11 @@ permissions:
users: [] users: []
allowed_permissions: [read] allowed_permissions: [read]
uri: /v1.0/service-tasks uri: /v1.0/service-tasks
user-groups-for-current-user:
groups: [everybody]
users: []
allowed_permissions: [read]
uri: /v1.0/user-groups/for-current-user
# read all for everybody # read all for everybody

View File

@ -15,7 +15,6 @@ groups:
jarrad, jarrad,
elizabeth, elizabeth,
jon, jon,
natalia,
] ]
Finance Team: Finance Team:
@ -31,7 +30,6 @@ groups:
jarrad, jarrad,
elizabeth, elizabeth,
jon, jon,
natalia,
sasha, sasha,
fin, fin,
fin1, fin1,
@ -56,6 +54,8 @@ groups:
core, core,
harmeet, harmeet,
] ]
test:
users: [natalia]
permissions: permissions:
admin: admin:
@ -75,6 +75,11 @@ permissions:
users: [] users: []
allowed_permissions: [read] allowed_permissions: [read]
uri: /v1.0/service-tasks uri: /v1.0/service-tasks
user-groups-for-current-user:
groups: [everybody]
users: []
allowed_permissions: [read]
uri: /v1.0/user-groups/for-current-user
# read all for everybody # read all for everybody
@ -104,12 +109,6 @@ permissions:
allowed_permissions: [read] allowed_permissions: [read]
uri: /v1.0/processes uri: /v1.0/processes
task-data-read:
groups: [demo]
users: []
allowed_permissions: [read]
uri: /v1.0/task-data/*
manage-procurement-admin: manage-procurement-admin:
groups: ["Project Lead"] groups: ["Project Lead"]
@ -165,3 +164,9 @@ permissions:
users: [] users: []
allowed_permissions: [create, read] allowed_permissions: [create, read]
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
create-test-instances:
groups: ["test"]
users: []
allowed_permissions: [create, read]
uri: /v1.0/process-instances/misc:test:*

View File

@ -8,7 +8,7 @@ from flask_bpmn.models.db import SpiffworkflowBaseDBModel
@dataclass @dataclass
class SpiffLoggingModel(SpiffworkflowBaseDBModel): class SpiffLoggingModel(SpiffworkflowBaseDBModel):
"""LoggingModel.""" """SpiffLoggingModel."""
__tablename__ = "spiff_logging" __tablename__ = "spiff_logging"
id: int = db.Column(db.Integer, primary_key=True) id: int = db.Column(db.Integer, primary_key=True)

View File

@ -849,6 +849,7 @@ def process_instance_list(
user_filter: Optional[bool] = False, user_filter: Optional[bool] = False,
report_identifier: Optional[str] = None, report_identifier: Optional[str] = None,
report_id: Optional[int] = None, report_id: Optional[int] = None,
group_identifier: Optional[str] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_instance_list.""" """Process_instance_list."""
process_instance_report = ProcessInstanceReportService.report_with_identifier( process_instance_report = ProcessInstanceReportService.report_with_identifier(
@ -988,10 +989,16 @@ def process_instance_list(
process_instance_query = process_instance_query.filter( process_instance_query = process_instance_query.filter(
SpiffLoggingModel.spiff_step == SpiffStepDetailsModel.spiff_step SpiffLoggingModel.spiff_step == SpiffStepDetailsModel.spiff_step
) )
process_instance_query = process_instance_query.join( if group_identifier:
GroupModel, process_instance_query = process_instance_query.join(
GroupModel.id == SpiffStepDetailsModel.lane_assignment_id, GroupModel,
) GroupModel.identifier == group_identifier,
)
else:
process_instance_query = process_instance_query.join(
GroupModel,
GroupModel.id == SpiffStepDetailsModel.lane_assignment_id,
)
process_instance_query = process_instance_query.join( process_instance_query = process_instance_query.join(
UserGroupAssignmentModel, UserGroupAssignmentModel,
UserGroupAssignmentModel.group_id == GroupModel.id, UserGroupAssignmentModel.group_id == GroupModel.id,
@ -1352,7 +1359,7 @@ def task_list_for_my_open_processes(
def task_list_for_me(page: int = 1, per_page: int = 100) -> flask.wrappers.Response: def task_list_for_me(page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
"""Task_list_for_processes_started_by_others.""" """Task_list_for_me."""
return get_tasks( return get_tasks(
processes_started_by_user=False, processes_started_by_user=False,
has_lane_assignment_id=False, has_lane_assignment_id=False,
@ -1362,10 +1369,23 @@ def task_list_for_me(page: int = 1, per_page: int = 100) -> flask.wrappers.Respo
def task_list_for_my_groups( def task_list_for_my_groups(
page: int = 1, per_page: int = 100 group_identifier: Optional[str] = None, page: int = 1, per_page: int = 100
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Task_list_for_processes_started_by_others.""" """Task_list_for_my_groups."""
return get_tasks(processes_started_by_user=False, page=page, per_page=per_page) return get_tasks(
group_identifier=group_identifier,
processes_started_by_user=False,
page=page,
per_page=per_page,
)
def user_group_list_for_current_user() -> flask.wrappers.Response:
"""User_group_list_for_current_user."""
groups = g.user.groups
# TODO: filter out the default group and have a way to know what is the default group
group_identifiers = [i.identifier for i in groups if i.identifier != "everybody"]
return make_response(jsonify(sorted(group_identifiers)), 200)
def get_tasks( def get_tasks(
@ -1373,6 +1393,7 @@ def get_tasks(
has_lane_assignment_id: bool = True, has_lane_assignment_id: bool = True,
page: int = 1, page: int = 1,
per_page: int = 100, per_page: int = 100,
group_identifier: Optional[str] = None,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Get_tasks.""" """Get_tasks."""
user_id = g.user.id user_id = g.user.id
@ -1409,9 +1430,14 @@ def get_tasks(
), ),
) )
if has_lane_assignment_id: if has_lane_assignment_id:
active_tasks_query = active_tasks_query.filter( if group_identifier:
ActiveTaskModel.lane_assignment_id.is_not(None) # type: ignore active_tasks_query = active_tasks_query.filter(
) GroupModel.identifier == group_identifier
)
else:
active_tasks_query = active_tasks_query.filter(
ActiveTaskModel.lane_assignment_id.is_not(None) # type: ignore
)
else: else:
active_tasks_query = active_tasks_query.filter(ActiveTaskModel.lane_assignment_id.is_(None)) # type: ignore active_tasks_query = active_tasks_query.filter(ActiveTaskModel.lane_assignment_id.is_(None)) # type: ignore
@ -1440,11 +1466,44 @@ def get_tasks(
return make_response(jsonify(response_json), 200) return make_response(jsonify(response_json), 200)
def process_instance_task_list( def process_instance_task_list_without_task_data(
modified_process_model_identifier: str, modified_process_model_identifier: str,
process_instance_id: int, process_instance_id: int,
all_tasks: bool = False, all_tasks: bool = False,
spiff_step: int = 0, spiff_step: int = 0,
) -> flask.wrappers.Response:
"""Process_instance_task_list_without_task_data."""
return process_instance_task_list(
modified_process_model_identifier,
process_instance_id,
all_tasks,
spiff_step,
get_task_data=False,
)
def process_instance_task_list_with_task_data(
modified_process_model_identifier: str,
process_instance_id: int,
all_tasks: bool = False,
spiff_step: int = 0,
) -> flask.wrappers.Response:
"""Process_instance_task_list_with_task_data."""
return process_instance_task_list(
modified_process_model_identifier,
process_instance_id,
all_tasks,
spiff_step,
get_task_data=True,
)
def process_instance_task_list(
_modified_process_model_identifier: str,
process_instance_id: int,
all_tasks: bool = False,
spiff_step: int = 0,
get_task_data: bool = False,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_instance_task_list.""" """Process_instance_task_list."""
process_instance = find_process_instance_by_id_or_raise(process_instance_id) process_instance = find_process_instance_by_id_or_raise(process_instance_id)
@ -1475,7 +1534,8 @@ def process_instance_task_list(
tasks = [] tasks = []
for spiff_task in spiff_tasks: for spiff_task in spiff_tasks:
task = ProcessInstanceService.spiff_task_to_api_task(processor, spiff_task) task = ProcessInstanceService.spiff_task_to_api_task(processor, spiff_task)
task.data = spiff_task.data if get_task_data:
task.data = spiff_task.data
tasks.append(task) tasks.append(task)
return make_response(jsonify(tasks), 200) return make_response(jsonify(tasks), 200)

View File

@ -79,6 +79,7 @@ type OwnProps = {
textToShowIfEmpty?: string; textToShowIfEmpty?: string;
paginationClassName?: string; paginationClassName?: string;
autoReload?: boolean; autoReload?: boolean;
additionalParams?: string;
}; };
interface dateParameters { interface dateParameters {
@ -90,6 +91,7 @@ export default function ProcessInstanceListTable({
processModelFullIdentifier, processModelFullIdentifier,
paginationQueryParamPrefix, paginationQueryParamPrefix,
perPageOptions, perPageOptions,
additionalParams,
showReports = true, showReports = true,
reportIdentifier, reportIdentifier,
textToShowIfEmpty, textToShowIfEmpty,
@ -253,6 +255,10 @@ export default function ProcessInstanceListTable({
} }
); );
if (additionalParams) {
queryParamString += `&${additionalParams}`;
}
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/process-instances?${queryParamString}`, path: `/process-instances?${queryParamString}`,
successCallback: setProcessInstancesFromResult, successCallback: setProcessInstancesFromResult,
@ -320,6 +326,7 @@ export default function ProcessInstanceListTable({
processModelFullIdentifier, processModelFullIdentifier,
perPageOptions, perPageOptions,
reportIdentifier, reportIdentifier,
additionalParams,
]); ]);
// This sets the filter data using the saved reports returned from the initial instance_list query. // This sets the filter data using the saved reports returned from the initial instance_list query.

View File

@ -35,7 +35,7 @@ export default function ProcessModelListTiles({
setProcessModels(result.results); setProcessModels(result.results);
}; };
// only allow 10 for now until we get the backend only returning certain models for user execution // only allow 10 for now until we get the backend only returning certain models for user execution
let queryParams = '?per_page=20'; let queryParams = '?per_page=1000';
if (processGroup) { if (processGroup) {
queryParams = `${queryParams}&process_group_identifier=${processGroup.id}`; queryParams = `${queryParams}&process_group_identifier=${processGroup.id}`;
} else { } else {

View File

@ -70,6 +70,7 @@ type OwnProps = {
completedProcessInstanceTasks?: ProcessInstanceTask[] | null; completedProcessInstanceTasks?: ProcessInstanceTask[] | null;
saveDiagram?: (..._args: any[]) => any; saveDiagram?: (..._args: any[]) => any;
onDeleteFile?: (..._args: any[]) => any; onDeleteFile?: (..._args: any[]) => any;
isPrimaryFile?: boolean;
onSetPrimaryFile?: (..._args: any[]) => any; onSetPrimaryFile?: (..._args: any[]) => any;
diagramXML?: string | null; diagramXML?: string | null;
fileName?: string; fileName?: string;
@ -94,6 +95,7 @@ export default function ReactDiagramEditor({
completedProcessInstanceTasks, completedProcessInstanceTasks,
saveDiagram, saveDiagram,
onDeleteFile, onDeleteFile,
isPrimaryFile,
onSetPrimaryFile, onSetPrimaryFile,
diagramXML, diagramXML,
fileName, fileName,
@ -573,7 +575,7 @@ export default function ReactDiagramEditor({
a={targetUris.processModelFileShowPath} a={targetUris.processModelFileShowPath}
ability={ability} ability={ability}
> >
{fileName && ( {fileName && !isPrimaryFile && (
<ButtonWithConfirmation <ButtonWithConfirmation
description={`Delete file ${fileName}?`} description={`Delete file ${fileName}?`}
onConfirmation={handleDelete} onConfirmation={handleDelete}

View File

@ -0,0 +1,203 @@
import { useEffect, useState } from 'react';
// @ts-ignore
import { Button, Table } from '@carbon/react';
import { Link, useSearchParams } from 'react-router-dom';
import PaginationForTable from './PaginationForTable';
import {
convertSecondsToFormattedDateTime,
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
refreshAtInterval,
} from '../helpers';
import HttpService from '../services/HttpService';
import { PaginationObject, ProcessInstanceTask } from '../interfaces';
import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords';
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
const REFRESH_INTERVAL = 5;
const REFRESH_TIMEOUT = 600;
type OwnProps = {
apiPath: string;
tableTitle: string;
tableDescription: string;
additionalParams?: string;
paginationQueryParamPrefix?: string;
paginationClassName?: string;
autoReload?: boolean;
showStartedBy?: boolean;
showWaitingOn?: boolean;
textToShowIfEmpty?: string;
};
export default function TaskListTable({
apiPath,
tableTitle,
tableDescription,
additionalParams,
paginationQueryParamPrefix,
paginationClassName,
textToShowIfEmpty,
autoReload = false,
showStartedBy = true,
showWaitingOn = true,
}: OwnProps) {
const [searchParams] = useSearchParams();
const [tasks, setTasks] = useState<ProcessInstanceTask[] | null>(null);
const [pagination, setPagination] = useState<PaginationObject | null>(null);
useEffect(() => {
const getTasks = () => {
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
undefined,
paginationQueryParamPrefix
);
const setTasksFromResult = (result: any) => {
setTasks(result.results);
setPagination(result.pagination);
};
let params = `?per_page=${perPage}&page=${page}`;
if (additionalParams) {
params += `&${additionalParams}`;
}
HttpService.makeCallToBackend({
path: `${apiPath}${params}`,
successCallback: setTasksFromResult,
});
};
getTasks();
if (autoReload) {
return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
}
return undefined;
}, [
searchParams,
additionalParams,
apiPath,
paginationQueryParamPrefix,
autoReload,
]);
const buildTable = () => {
if (!tasks) {
return null;
}
const rows = tasks.map((row) => {
const rowToUse = row as any;
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
const modifiedProcessModelIdentifier =
modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier);
return (
<tr key={rowToUse.id}>
<td>
<Link
data-qa="process-instance-show-link"
to={`/admin/process-instances/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
title={`View process instance ${rowToUse.process_instance_id}`}
>
{rowToUse.process_instance_id}
</Link>
</td>
<td>
<Link
data-qa="process-model-show-link"
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
title={rowToUse.process_model_identifier}
>
{rowToUse.process_model_display_name}
</Link>
</td>
<td
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
>
{rowToUse.task_title}
</td>
{showStartedBy ? <td>{rowToUse.username}</td> : ''}
{showWaitingOn ? <td>{rowToUse.group_identifier || '-'}</td> : ''}
<td>
{convertSecondsToFormattedDateTime(
rowToUse.created_at_in_seconds
) || '-'}
</td>
<TableCellWithTimeAgoInWords
timeInSeconds={rowToUse.updated_at_in_seconds}
/>
<td>
<Button
variant="primary"
href={taskUrl}
hidden={rowToUse.process_instance_status === 'suspended'}
disabled={!rowToUse.current_user_is_potential_owner}
>
Go
</Button>
</td>
</tr>
);
});
let tableHeaders = ['Id', 'Process', 'Task'];
if (showStartedBy) {
tableHeaders.push('Started By');
}
if (showWaitingOn) {
tableHeaders.push('Waiting For');
}
tableHeaders = tableHeaders.concat([
'Date Started',
'Last Updated',
'Actions',
]);
return (
<Table striped bordered>
<thead>
<tr>
{tableHeaders.map((tableHeader: string) => {
return <th>{tableHeader}</th>;
})}
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
);
};
const tasksComponent = () => {
if (pagination && pagination.total < 1) {
return (
<p className="no-results-message with-large-bottom-margin">
{textToShowIfEmpty}
</p>
);
}
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
undefined,
paginationQueryParamPrefix
);
return (
<PaginationForTable
page={page}
perPage={perPage}
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
pagination={pagination}
tableToDisplay={buildTable()}
paginationQueryParamPrefix={paginationQueryParamPrefix}
paginationClassName={paginationClassName}
/>
);
};
if (tasks) {
return (
<>
<h2>{tableTitle}</h2>
<p className="data-table-description">{tableDescription}</p>
{tasksComponent()}
</>
);
}
return null;
}

View File

@ -1,156 +1,18 @@
import { useEffect, useState } from 'react'; import TaskListTable from './TaskListTable';
// @ts-ignore
import { Button, Table } from '@carbon/react';
import { Link, useSearchParams } from 'react-router-dom';
import PaginationForTable from './PaginationForTable';
import {
convertSecondsToFormattedDateTime,
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
refreshAtInterval,
} from '../helpers';
import HttpService from '../services/HttpService';
import { PaginationObject } from '../interfaces';
import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords';
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
const paginationQueryParamPrefix = 'tasks_for_my_open_processes'; const paginationQueryParamPrefix = 'tasks_for_my_open_processes';
const REFRESH_INTERVAL = 5;
const REFRESH_TIMEOUT = 600;
export default function MyOpenProcesses() { export default function MyOpenProcesses() {
const [searchParams] = useSearchParams();
const [tasks, setTasks] = useState([]);
const [pagination, setPagination] = useState<PaginationObject | null>(null);
useEffect(() => {
const getTasks = () => {
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
undefined,
paginationQueryParamPrefix
);
const setTasksFromResult = (result: any) => {
setTasks(result.results);
setPagination(result.pagination);
};
HttpService.makeCallToBackend({
path: `/tasks/for-my-open-processes?per_page=${perPage}&page=${page}`,
successCallback: setTasksFromResult,
});
};
getTasks();
return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
}, [searchParams]);
const buildTable = () => {
const rows = tasks.map((row) => {
const rowToUse = row as any;
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
const modifiedProcessModelIdentifier =
modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier);
return (
<tr key={rowToUse.id}>
<td>
<Link
data-qa="process-instance-show-link"
to={`/admin/process-instances/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
title={`View process instance ${rowToUse.process_instance_id}`}
>
{rowToUse.process_instance_id}
</Link>
</td>
<td>
<Link
data-qa="process-model-show-link"
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
title={rowToUse.process_model_identifier}
>
{rowToUse.process_model_display_name}
</Link>
</td>
<td
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
>
{rowToUse.task_title}
</td>
<td>{rowToUse.group_identifier || '-'}</td>
<td>
{convertSecondsToFormattedDateTime(
rowToUse.created_at_in_seconds
) || '-'}
</td>
<TableCellWithTimeAgoInWords
timeInSeconds={rowToUse.updated_at_in_seconds}
/>
<td>
<Button
variant="primary"
href={taskUrl}
hidden={rowToUse.process_instance_status === 'suspended'}
disabled={!rowToUse.current_user_is_potential_owner}
>
Go
</Button>
</td>
</tr>
);
});
return (
<Table striped bordered>
<thead>
<tr>
<th>Id</th>
<th>Process</th>
<th>Task</th>
<th>Waiting For</th>
<th>Date Started</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
);
};
const tasksComponent = () => {
if (pagination && pagination.total < 1) {
return (
<p className="no-results-message with-large-bottom-margin">
There are no tasks for processes you started at this time.
</p>
);
}
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
undefined,
paginationQueryParamPrefix
);
return (
<PaginationForTable
page={page}
perPage={perPage}
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
pagination={pagination}
tableToDisplay={buildTable()}
paginationQueryParamPrefix={paginationQueryParamPrefix}
paginationClassName="with-large-bottom-margin"
/>
);
};
return ( return (
<> <TaskListTable
<h2>My open instances</h2> apiPath="/tasks/for-my-open-processes"
<p className="data-table-description"> paginationQueryParamPrefix={paginationQueryParamPrefix}
These tasks are for processes you started which are not complete. You tableTitle="Tasks for my open instances"
may not have an action to take at this time. See below for tasks waiting tableDescription="These tasks are for processes you started which are not complete. You may not have an action to take at this time. See below for tasks waiting on you."
on you. paginationClassName="with-large-bottom-margin"
</p> textToShowIfEmpty="There are no tasks for processes you started at this time."
{tasksComponent()} autoReload
</> showStartedBy={false}
/>
); );
} }

View File

@ -1,149 +1,16 @@
import { useEffect, useState } from 'react'; import TaskListTable from './TaskListTable';
// @ts-ignore
import { Button, Table } from '@carbon/react';
import { Link, useSearchParams } from 'react-router-dom';
import PaginationForTable from './PaginationForTable';
import {
convertSecondsToFormattedDateTime,
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
} from '../helpers';
import HttpService from '../services/HttpService';
import { PaginationObject } from '../interfaces';
import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords';
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
export default function TasksWaitingForMe() { export default function TasksWaitingForMe() {
const [searchParams] = useSearchParams();
const [tasks, setTasks] = useState([]);
const [pagination, setPagination] = useState<PaginationObject | null>(null);
useEffect(() => {
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
undefined,
'tasks_waiting_for_me'
);
const setTasksFromResult = (result: any) => {
setTasks(result.results);
setPagination(result.pagination);
};
HttpService.makeCallToBackend({
path: `/tasks/for-me?per_page=${perPage}&page=${page}`,
successCallback: setTasksFromResult,
});
}, [searchParams]);
const buildTable = () => {
const rows = tasks.map((row) => {
const rowToUse = row as any;
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`;
const modifiedProcessModelIdentifier =
modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier);
return (
<tr key={rowToUse.id}>
<td>
<Link
data-qa="process-instance-show-link"
to={`/admin/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
title={`View process instance ${rowToUse.process_instance_id}`}
>
{rowToUse.process_instance_id}
</Link>
</td>
<td>
<Link
data-qa="process-model-show-link"
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
title={rowToUse.process_model_identifier}
>
{rowToUse.process_model_display_name}
</Link>
</td>
<td
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
>
{rowToUse.task_title}
</td>
<td>{rowToUse.username}</td>
<td>{rowToUse.group_identifier || '-'}</td>
<td>
{convertSecondsToFormattedDateTime(
rowToUse.created_at_in_seconds
) || '-'}
</td>
<TableCellWithTimeAgoInWords
timeInSeconds={rowToUse.updated_at_in_seconds}
/>
<td>
<Button
variant="primary"
href={taskUrl}
hidden={rowToUse.process_instance_status === 'suspended'}
disabled={!rowToUse.current_user_is_potential_owner}
>
Go
</Button>
</td>
</tr>
);
});
return (
<Table striped bordered>
<thead>
<tr>
<th>Id</th>
<th>Process</th>
<th>Task</th>
<th>Started By</th>
<th>Waiting For</th>
<th>Date Started</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
);
};
const tasksComponent = () => {
if (pagination && pagination.total < 1) {
return (
<p className="no-results-message with-large-bottom-margin">
You have no task assignments at this time.
</p>
);
}
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
undefined,
'tasks_waiting_for_me'
);
return (
<PaginationForTable
page={page}
perPage={perPage}
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
pagination={pagination}
tableToDisplay={buildTable()}
paginationQueryParamPrefix="tasks_waiting_for_me"
paginationClassName="with-large-bottom-margin"
/>
);
};
return ( return (
<> <TaskListTable
<h2>Tasks waiting for me</h2> apiPath="/tasks/for-me"
<p className="data-table-description"> paginationQueryParamPrefix="tasks_waiting_for_me"
These processes are waiting on you to complete the next task. All are tableTitle="Tasks waiting for me"
processes created by others that are now actionable by you. tableDescription="These processes are waiting on you to complete the next task. All are processes created by others that are now actionable by you."
</p> paginationClassName="with-large-bottom-margin"
{tasksComponent()} textToShowIfEmpty="No tasks are waiting for you."
</> autoReload
showWaitingOn={false}
/>
); );
} }

View File

@ -1,156 +1,41 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
// @ts-ignore
import { Button, Table } from '@carbon/react';
import { Link, useSearchParams } from 'react-router-dom';
import PaginationForTable from './PaginationForTable';
import {
convertSecondsToFormattedDateTime,
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
refreshAtInterval,
} from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { PaginationObject } from '../interfaces'; import TaskListTable from './TaskListTable';
import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords';
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
const paginationQueryParamPrefix = 'tasks_waiting_for_my_groups';
const REFRESH_INTERVAL = 5;
const REFRESH_TIMEOUT = 600;
export default function TasksWaitingForMyGroups() { export default function TasksWaitingForMyGroups() {
const [searchParams] = useSearchParams(); const [userGroups, setUserGroups] = useState<string[] | null>(null);
const [tasks, setTasks] = useState([]);
const [pagination, setPagination] = useState<PaginationObject | null>(null);
useEffect(() => { useEffect(() => {
const getTasks = () => { HttpService.makeCallToBackend({
const { page, perPage } = getPageInfoFromSearchParams( path: `/user-groups/for-current-user`,
searchParams, successCallback: setUserGroups,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE, });
undefined, }, [setUserGroups]);
paginationQueryParamPrefix
);
const setTasksFromResult = (result: any) => {
setTasks(result.results);
setPagination(result.pagination);
};
HttpService.makeCallToBackend({
path: `/tasks/for-my-groups?per_page=${perPage}&page=${page}`,
successCallback: setTasksFromResult,
});
};
getTasks();
return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
}, [searchParams]);
const buildTable = () => { const tableComponents = () => {
const rows = tasks.map((row) => { if (!userGroups) {
const rowToUse = row as any; return null;
const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.task_id}`; }
const modifiedProcessModelIdentifier =
modifyProcessIdentifierForPathParam(rowToUse.process_model_identifier); return userGroups.map((userGroup: string) => {
return ( return (
<tr key={rowToUse.id}> <TaskListTable
<td> apiPath="/tasks/for-my-groups"
<Link additionalParams={`group_identifier=${userGroup}`}
data-qa="process-instance-show-link" paginationQueryParamPrefix={`group-tasks-${userGroup}`}
to={`/admin/process-instances/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`} tableTitle={`Tasks waiting for group: ${userGroup}`}
title={`View process instance ${rowToUse.process_instance_id}`} tableDescription={`This is a list of tasks for the ${userGroup} group. They can be completed by any member of the group.`}
> paginationClassName="with-large-bottom-margin"
{rowToUse.process_instance_id} textToShowIfEmpty="This group has no task assignments at this time."
</Link> autoReload
</td> showWaitingOn={false}
<td> />
<Link
data-qa="process-model-show-link"
to={`/admin/process-models/${modifiedProcessModelIdentifier}`}
title={rowToUse.process_model_identifier}
>
{rowToUse.process_model_display_name}
</Link>
</td>
<td
title={`task id: ${rowToUse.name}, spiffworkflow task guid: ${rowToUse.id}`}
>
{rowToUse.task_title}
</td>
<td>{rowToUse.username}</td>
<td>{rowToUse.group_identifier || '-'}</td>
<td>
{convertSecondsToFormattedDateTime(
rowToUse.created_at_in_seconds
) || '-'}
</td>
<TableCellWithTimeAgoInWords
timeInSeconds={rowToUse.updated_at_in_seconds}
/>
<td>
<Button
variant="primary"
href={taskUrl}
hidden={rowToUse.process_instance_status === 'suspended'}
disabled={!rowToUse.current_user_is_potential_owner}
>
Go
</Button>
</td>
</tr>
); );
}); });
return (
<Table striped bordered>
<thead>
<tr>
<th>Id</th>
<th>Process</th>
<th>Task</th>
<th>Started By</th>
<th>Waiting For</th>
<th>Date Started</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
);
}; };
const tasksComponent = () => { if (userGroups) {
if (pagination && pagination.total < 1) { return <>{tableComponents()}</>;
return ( }
<p className="no-results-message"> return null;
Your groups have no task assignments at this time.
</p>
);
}
const { page, perPage } = getPageInfoFromSearchParams(
searchParams,
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
undefined,
paginationQueryParamPrefix
);
return (
<PaginationForTable
page={page}
perPage={perPage}
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
pagination={pagination}
tableToDisplay={buildTable()}
paginationQueryParamPrefix={paginationQueryParamPrefix}
/>
);
};
return (
<>
<h2>Tasks waiting for my groups</h2>
<p className="data-table-description">
This is a list of tasks for groups you belong to that can be completed
by any member of the group.
</p>
{tasksComponent()}
</>
);
} }

View File

@ -14,7 +14,8 @@ export const useUriListForPermissions = () => {
processInstanceListPath: '/v1.0/process-instances', processInstanceListPath: '/v1.0/process-instances',
processInstanceLogListPath: `/v1.0/logs/${params.process_model_id}/${params.process_instance_id}`, processInstanceLogListPath: `/v1.0/logs/${params.process_model_id}/${params.process_instance_id}`,
processInstanceReportListPath: '/v1.0/process-instances/reports', processInstanceReportListPath: '/v1.0/process-instances/reports',
processInstanceTaskListPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`, processInstanceTaskListPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}/task-info`,
processInstanceTaskListDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`,
processModelCreatePath: `/v1.0/process-models/${params.process_group_id}`, processModelCreatePath: `/v1.0/process-models/${params.process_group_id}`,
processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`, processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`,
processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`, processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`,

View File

@ -13,6 +13,12 @@ export interface RecentProcessModel {
export interface ProcessInstanceTask { export interface ProcessInstanceTask {
id: string; id: string;
process_model_display_name: string;
process_model_identifier: string;
task_title: string;
lane_assignment_id: string;
process_instance_status: number;
updated_at_in_seconds: number;
state: string; state: string;
process_identifier: string; process_identifier: string;
name: string; name: string;

View File

@ -1,6 +1,45 @@
import { useEffect, useState } from 'react';
import ProcessInstanceListTable from '../components/ProcessInstanceListTable'; import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
import HttpService from '../services/HttpService';
export default function CompletedInstances() { export default function CompletedInstances() {
const [userGroups, setUserGroups] = useState<string[] | null>(null);
useEffect(() => {
HttpService.makeCallToBackend({
path: `/user-groups/for-current-user`,
successCallback: setUserGroups,
});
}, [setUserGroups]);
const groupTableComponents = () => {
if (!userGroups) {
return null;
}
return userGroups.map((userGroup: string) => {
return (
<>
<h2>With tasks completed by group: {userGroup}</h2>
<p className="data-table-description">
This is a list of instances with tasks that were completed by the{' '}
{userGroup} group.
</p>
<ProcessInstanceListTable
filtersEnabled={false}
paginationQueryParamPrefix="group_completed_instances"
paginationClassName="with-large-bottom-margin"
perPageOptions={[2, 5, 25]}
reportIdentifier="system_report_instances_with_tasks_completed_by_my_groups"
showReports={false}
textToShowIfEmpty="This group has no completed instances at this time."
additionalParams={`group_identifier=${userGroup}`}
/>
</>
);
});
};
return ( return (
<> <>
<h2>My completed instances</h2> <h2>My completed instances</h2>
@ -17,7 +56,7 @@ export default function CompletedInstances() {
paginationClassName="with-large-bottom-margin" paginationClassName="with-large-bottom-margin"
autoReload autoReload
/> />
<h2>Tasks completed by me</h2> <h2>With tasks completed by me</h2>
<p className="data-table-description"> <p className="data-table-description">
This is a list of instances where you have completed tasks. This is a list of instances where you have completed tasks.
</p> </p>
@ -27,22 +66,10 @@ export default function CompletedInstances() {
perPageOptions={[2, 5, 25]} perPageOptions={[2, 5, 25]}
reportIdentifier="system_report_instances_with_tasks_completed_by_me" reportIdentifier="system_report_instances_with_tasks_completed_by_me"
showReports={false} showReports={false}
textToShowIfEmpty="You have no completed tasks at this time." textToShowIfEmpty="You have no completed instances at this time."
paginationClassName="with-large-bottom-margin" paginationClassName="with-large-bottom-margin"
/> />
<h2>Tasks completed by my groups</h2> {groupTableComponents()}
<p className="data-table-description">
This is a list of instances with tasks that were completed by groups you
belong to.
</p>
<ProcessInstanceListTable
filtersEnabled={false}
paginationQueryParamPrefix="group_completed_tasks"
perPageOptions={[2, 5, 25]}
reportIdentifier="system_report_instances_with_tasks_completed_by_my_groups"
showReports={false}
textToShowIfEmpty="Your group has no completed tasks at this time."
/>
</> </>
); );
} }

View File

@ -76,11 +76,13 @@ export default function ProcessInstanceShow() {
const permissionRequestData: PermissionsToCheck = { const permissionRequestData: PermissionsToCheck = {
[targetUris.messageInstanceListPath]: ['GET'], [targetUris.messageInstanceListPath]: ['GET'],
[targetUris.processInstanceTaskListPath]: ['GET'], [targetUris.processInstanceTaskListPath]: ['GET'],
[targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'],
[targetUris.processInstanceActionPath]: ['DELETE'], [targetUris.processInstanceActionPath]: ['DELETE'],
[targetUris.processInstanceLogListPath]: ['GET'], [targetUris.processInstanceLogListPath]: ['GET'],
[`${targetUris.processInstanceActionPath}/suspend`]: ['PUT'], [targetUris.processModelShowPath]: ['PUT'],
[`${targetUris.processInstanceActionPath}/terminate`]: ['PUT'], [`${targetUris.processInstanceActionPath}/suspend`]: ['POST'],
[`${targetUris.processInstanceActionPath}/resume`]: ['PUT'], [`${targetUris.processInstanceActionPath}/terminate`]: ['POST'],
[`${targetUris.processInstanceActionPath}/resume`]: ['POST'],
}; };
const { ability, permissionsLoaded } = usePermissionFetcher( const { ability, permissionsLoaded } = usePermissionFetcher(
permissionRequestData permissionRequestData
@ -110,9 +112,15 @@ export default function ProcessInstanceShow() {
if (typeof params.spiff_step !== 'undefined') { if (typeof params.spiff_step !== 'undefined') {
taskParams = `${taskParams}&spiff_step=${params.spiff_step}`; taskParams = `${taskParams}&spiff_step=${params.spiff_step}`;
} }
if (ability.can('GET', targetUris.processInstanceTaskListPath)) { let taskPath = '';
if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) {
taskPath = `${targetUris.processInstanceTaskListDataPath}${taskParams}`;
} else if (ability.can('GET', targetUris.processInstanceTaskListPath)) {
taskPath = `${targetUris.processInstanceTaskListPath}${taskParams}`;
}
if (taskPath) {
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `${targetUris.processInstanceTaskListPath}${taskParams}`, path: taskPath,
successCallback: setTasks, successCallback: setTasks,
failureCallback: processTaskFailure, failureCallback: processTaskFailure,
}); });
@ -448,7 +456,9 @@ export default function ProcessInstanceShow() {
const canEditTaskData = (task: any) => { const canEditTaskData = (task: any) => {
return ( return (
task.state === 'READY' && showingLastSpiffStep(processInstance as any) ability.can('PUT', targetUris.processInstanceTaskListDataPath) &&
task.state === 'READY' &&
showingLastSpiffStep(processInstance as any)
); );
}; };
@ -539,7 +549,10 @@ export default function ProcessInstanceShow() {
const taskDataButtons = (task: any) => { const taskDataButtons = (task: any) => {
const buttons = []; const buttons = [];
if (task.type === 'Script Task') { if (
task.type === 'Script Task' &&
ability.can('PUT', targetUris.processModelShowPath)
) {
buttons.push( buttons.push(
<Button <Button
data-qa="create-script-unit-test-button" data-qa="create-script-unit-test-button"

View File

@ -25,6 +25,7 @@ import {
ProcessReference, ProcessReference,
} from '../interfaces'; } from '../interfaces';
import ProcessSearch from '../components/ProcessSearch'; import ProcessSearch from '../components/ProcessSearch';
import { Notification } from '../components/Notification';
export default function ProcessModelEditDiagram() { export default function ProcessModelEditDiagram() {
const [showFileNameEditor, setShowFileNameEditor] = useState(false); const [showFileNameEditor, setShowFileNameEditor] = useState(false);
@ -157,6 +158,8 @@ export default function ProcessModelEditDiagram() {
} }
}; };
const [displaySaveFileMessage, setDisplaySaveFileMessage] =
useState<boolean>(false);
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => { const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
setErrorMessage(null); setErrorMessage(null);
setBpmnXmlForDiagramRendering(bpmnXML); setBpmnXmlForDiagramRendering(bpmnXML);
@ -192,6 +195,7 @@ export default function ProcessModelEditDiagram() {
// after saving the file, make sure we null out newFileName // after saving the file, make sure we null out newFileName
// so it does not get used over the params // so it does not get used over the params
setNewFileName(''); setNewFileName('');
setDisplaySaveFileMessage(true);
}; };
const onDeleteFile = (fileName = params.file_name) => { const onDeleteFile = (fileName = params.file_name) => {
@ -819,6 +823,7 @@ export default function ProcessModelEditDiagram() {
processModelId={params.process_model_id || ''} processModelId={params.process_model_id || ''}
saveDiagram={saveDiagram} saveDiagram={saveDiagram}
onDeleteFile={onDeleteFile} onDeleteFile={onDeleteFile}
isPrimaryFile={params.file_name === processModel?.primary_file_name}
onSetPrimaryFile={onSetPrimaryFileCallback} onSetPrimaryFile={onSetPrimaryFileCallback}
diagramXML={bpmnXmlForDiagramRendering} diagramXML={bpmnXmlForDiagramRendering}
fileName={params.file_name} fileName={params.file_name}
@ -836,6 +841,20 @@ export default function ProcessModelEditDiagram() {
); );
}; };
const saveFileMessage = () => {
if (displaySaveFileMessage) {
return (
<Notification
title="File Saved: "
onClose={() => setDisplaySaveFileMessage(false)}
>
Changes to the file were saved.
</Notification>
);
}
return null;
};
// if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it // if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it
if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) { if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) {
const processModelFileName = processModelFile ? processModelFile.name : ''; const processModelFileName = processModelFile ? processModelFile.name : '';
@ -856,6 +875,7 @@ export default function ProcessModelEditDiagram() {
Process Model File{processModelFile ? ': ' : ''} Process Model File{processModelFile ? ': ' : ''}
{processModelFileName} {processModelFileName}
</h1> </h1>
{saveFileMessage()}
{appropriateEditor()} {appropriateEditor()}
{newFileNameBox()} {newFileNameBox()}
{scriptEditor()} {scriptEditor()}

View File

@ -264,25 +264,27 @@ export default function ProcessModelShow() {
</Can> </Can>
); );
elements.push( if (!isPrimaryBpmnFile) {
<Can elements.push(
I="DELETE" <Can
a={targetUris.processModelFileCreatePath} I="DELETE"
ability={ability} a={targetUris.processModelFileCreatePath}
> ability={ability}
<ButtonWithConfirmation >
kind="ghost" <ButtonWithConfirmation
renderIcon={TrashCan} kind="ghost"
iconDescription="Delete File" renderIcon={TrashCan}
hasIconOnly iconDescription="Delete File"
description={`Delete file: ${processModelFile.name}`} hasIconOnly
onConfirmation={() => { description={`Delete file: ${processModelFile.name}`}
onDeleteFile(processModelFile.name); onConfirmation={() => {
}} onDeleteFile(processModelFile.name);
confirmButtonLabel="Delete" }}
/> confirmButtonLabel="Delete"
</Can> />
); </Can>
);
}
if (processModelFile.name.match(/\.bpmn$/) && !isPrimaryBpmnFile) { if (processModelFile.name.match(/\.bpmn$/) && !isPrimaryBpmnFile) {
elements.push( elements.push(
<Can I="PUT" a={targetUris.processModelShowPath} ability={ability}> <Can I="PUT" a={targetUris.processModelShowPath} ability={ability}>
@ -360,27 +362,82 @@ export default function ProcessModelShow() {
); );
}; };
const [fileUploadEvent, setFileUploadEvent] = useState(null);
const [duplicateFilename, setDuplicateFilename] = useState<String>('');
const [showOverwriteConfirmationPrompt, setShowOverwriteConfirmationPrompt] =
useState(false);
const doFileUpload = (event: any) => {
event.preventDefault();
const url = `/process-models/${modifiedProcessModelId}/files`;
const formData = new FormData();
formData.append('file', filesToUpload[0]);
formData.append('fileName', filesToUpload[0].name);
HttpService.makeCallToBackend({
path: url,
successCallback: onUploadedCallback,
httpMethod: 'POST',
postBody: formData,
});
setFilesToUpload(null);
};
const handleFileUploadCancel = () => { const handleFileUploadCancel = () => {
setShowFileUploadModal(false); setShowFileUploadModal(false);
setFilesToUpload(null); setFilesToUpload(null);
}; };
const handleOverwriteFileConfirm = () => {
setShowOverwriteConfirmationPrompt(false);
doFileUpload(fileUploadEvent);
};
const handleOverwriteFileCancel = () => {
setShowOverwriteConfirmationPrompt(false);
setFilesToUpload(null);
};
const confirmOverwriteFileDialog = () => {
return (
<Modal
danger
open={showOverwriteConfirmationPrompt}
data-qa="file-overwrite-modal-confirmation-dialog"
modalHeading={`Overwrite the file: ${duplicateFilename}`}
modalLabel="Overwrite file?"
primaryButtonText="Yes"
secondaryButtonText="Cancel"
onSecondarySubmit={handleOverwriteFileCancel}
onRequestSubmit={handleOverwriteFileConfirm}
onRequestClose={handleOverwriteFileCancel}
/>
);
};
const displayOverwriteConfirmation = (filename: String) => {
setDuplicateFilename(filename);
setShowOverwriteConfirmationPrompt(true);
};
const checkDuplicateFile = (event: any) => {
if (processModel && processModel.files.length > 0) {
let foundExistingFile = false;
processModel.files.forEach((file) => {
if (file.name === filesToUpload[0].name) {
foundExistingFile = true;
}
});
if (foundExistingFile) {
displayOverwriteConfirmation(filesToUpload[0].name);
setFileUploadEvent(event);
} else {
doFileUpload(event);
}
}
};
const handleFileUpload = (event: any) => { const handleFileUpload = (event: any) => {
if (processModel) { if (processModel) {
event.preventDefault(); checkDuplicateFile(event);
const url = `/process-models/${modifiedProcessModelId}/files`;
const formData = new FormData();
formData.append('file', filesToUpload[0]);
formData.append('fileName', filesToUpload[0].name);
HttpService.makeCallToBackend({
path: url,
successCallback: onUploadedCallback,
httpMethod: 'POST',
postBody: formData,
});
} }
setShowFileUploadModal(false); setShowFileUploadModal(false);
setFilesToUpload(null);
}; };
const fileUploadModal = () => { const fileUploadModal = () => {
@ -548,6 +605,7 @@ export default function ProcessModelShow() {
return ( return (
<> <>
{fileUploadModal()} {fileUploadModal()}
{confirmOverwriteFileDialog()}
<ProcessBreadcrumb <ProcessBreadcrumb
hotCrumbs={[ hotCrumbs={[
['Process Groups', '/admin'], ['Process Groups', '/admin'],

View File

@ -40,7 +40,7 @@ export default function TaskShow() {
const { targetUris } = useUriListForPermissions(); const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = { const permissionRequestData: PermissionsToCheck = {
[targetUris.processInstanceTaskListPath]: ['GET'], [targetUris.processInstanceTaskListDataPath]: ['GET'],
}; };
const { ability, permissionsLoaded } = usePermissionFetcher( const { ability, permissionsLoaded } = usePermissionFetcher(
permissionRequestData permissionRequestData
@ -50,7 +50,7 @@ export default function TaskShow() {
if (permissionsLoaded) { if (permissionsLoaded) {
const processResult = (result: any) => { const processResult = (result: any) => {
setTask(result); setTask(result);
if (ability.can('GET', targetUris.processInstanceTaskListPath)) { if (ability.can('GET', targetUris.processInstanceTaskListDataPath)) {
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/task-data/${modifyProcessIdentifierForPathParam( path: `/task-data/${modifyProcessIdentifierForPathParam(
result.process_model_identifier result.process_model_identifier