diff --git a/flask-bpmn/src/flask_bpmn/api/api_error.py b/flask-bpmn/src/flask_bpmn/api/api_error.py index eb390abe..c782c2d3 100644 --- a/flask-bpmn/src/flask_bpmn/api/api_error.py +++ b/flask-bpmn/src/flask_bpmn/api/api_error.py @@ -170,15 +170,17 @@ def set_user_sentry_context() -> None: def handle_exception(exception: Exception) -> flask.wrappers.Response: """Handles unexpected exceptions.""" 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 - if organization_slug and project_slug: - sentry_link = ( - f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}" - ) + if not isinstance(exception, ApiError) or exception.error_code != "invalid_token": + id = capture_exception(exception) + + 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 # seems to break the sentry sdk context where we no longer get back diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index a23004b4..5bcb2d0f 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -654,7 +654,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "860f2387bebdaa9220e9fbf6f8fa7f74e805d0d4" +resolved_reference = "0f2d249d0e799bec912d46132e9ef9754fdacbd7" [[package]] name = "Flask-Cors" @@ -1851,7 +1851,7 @@ lxml = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "ffb1686757f944065580dd2db8def73d6c1f0134" +resolved_reference = "841bd63017bb1d92858456393f144b4e5b23c994" [[package]] 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_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-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-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"}, {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_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-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-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"}, {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_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-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-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"}, {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_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"}, {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-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_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"}, @@ -2989,18 +2989,7 @@ psycopg2 = [ {file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"}, ] 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 = [ diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index c559ba98..be9796aa 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -595,6 +595,12 @@ paths: description: Specifies the identifier of a report to use, if any schema: 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: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_list summary: Returns a list of process instances for a given process model @@ -673,6 +679,53 @@ paths: schema: $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}: parameters: - name: modified_process_model_identifier @@ -1076,6 +1129,12 @@ paths: /tasks/for-my-groups: parameters: + - name: group_identifier + in: query + required: false + description: The identifier of the group to get the tasks for + schema: + type: string - name: page in: query required: false @@ -1103,6 +1162,22 @@ paths: items: $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}: parameters: - name: modified_process_model_identifier @@ -1132,8 +1207,8 @@ paths: get: tags: - Process Instances - operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_task_list - summary: returns the list of all user tasks associated with process instance + 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 with the task data responses: "200": description: list of tasks diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 0d480c53..566ba1ea 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -48,6 +48,7 @@ groups: fin, fin1, harmeet, + jason, sasha, manuchehr, lead, @@ -93,6 +94,11 @@ permissions: users: [] allowed_permissions: [read] 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-process-groups: @@ -120,12 +126,12 @@ permissions: users: [] allowed_permissions: [read] uri: /v1.0/processes - - task-data-read: - groups: [demo] - users: [] - allowed_permissions: [read] - uri: /v1.0/task-data/* + # + # task-data-read: + # groups: [demo] + # users: [] + # allowed_permissions: [read] + # uri: /v1.0/task-data/* manage-procurement-admin: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml index 2e41e3b0..2adf3d9c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml @@ -15,7 +15,6 @@ groups: jarrad, elizabeth, jon, - natalia, ] Finance Team: @@ -31,7 +30,6 @@ groups: jarrad, elizabeth, jon, - natalia, sasha, fin, fin1, @@ -56,6 +54,8 @@ groups: core, harmeet, ] + test: + users: [natalia] permissions: admin: @@ -148,35 +148,26 @@ permissions: allowed_permissions: [create, read, update, delete] 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: groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] 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: groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] 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: groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] 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:* diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py index b0b90887..532a6c09 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py @@ -8,7 +8,7 @@ from flask_bpmn.models.db import SpiffworkflowBaseDBModel @dataclass class SpiffLoggingModel(SpiffworkflowBaseDBModel): - """LoggingModel.""" + """SpiffLoggingModel.""" __tablename__ = "spiff_logging" id: int = db.Column(db.Integer, primary_key=True) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index cf41767b..31e69216 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -849,6 +849,7 @@ def process_instance_list( user_filter: Optional[bool] = False, report_identifier: Optional[str] = None, report_id: Optional[int] = None, + group_identifier: Optional[str] = None, ) -> flask.wrappers.Response: """Process_instance_list.""" process_instance_report = ProcessInstanceReportService.report_with_identifier( @@ -988,10 +989,16 @@ def process_instance_list( process_instance_query = process_instance_query.filter( SpiffLoggingModel.spiff_step == SpiffStepDetailsModel.spiff_step ) - process_instance_query = process_instance_query.join( - GroupModel, - GroupModel.id == SpiffStepDetailsModel.lane_assignment_id, - ) + if group_identifier: + process_instance_query = process_instance_query.join( + 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( UserGroupAssignmentModel, 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: - """Task_list_for_processes_started_by_others.""" + """Task_list_for_me.""" return get_tasks( processes_started_by_user=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( - page: int = 1, per_page: int = 100 + group_identifier: Optional[str] = None, page: int = 1, per_page: int = 100 ) -> flask.wrappers.Response: - """Task_list_for_processes_started_by_others.""" - return get_tasks(processes_started_by_user=False, page=page, per_page=per_page) + """Task_list_for_my_groups.""" + 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( @@ -1373,6 +1393,7 @@ def get_tasks( has_lane_assignment_id: bool = True, page: int = 1, per_page: int = 100, + group_identifier: Optional[str] = None, ) -> flask.wrappers.Response: """Get_tasks.""" user_id = g.user.id @@ -1409,9 +1430,14 @@ def get_tasks( ), ) if has_lane_assignment_id: - active_tasks_query = active_tasks_query.filter( - ActiveTaskModel.lane_assignment_id.is_not(None) # type: ignore - ) + if group_identifier: + 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: 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) -def process_instance_task_list( +def process_instance_task_list_without_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_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: """Process_instance_task_list.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) @@ -1475,7 +1534,8 @@ def process_instance_task_list( tasks = [] for spiff_task in spiff_tasks: task = ProcessInstanceService.spiff_task_to_api_task(spiff_task) - task.data = spiff_task.data + if get_task_data: + task.data = spiff_task.data tasks.append(task) return make_response(jsonify(tasks), 200) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 4a0100d3..3bc21456 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -2550,7 +2550,7 @@ class TestProcessApi(BaseTest): f"/v1.0/process-models/{modified_original_process_model_id}/move?new_location={new_location}", headers=self.logged_in_headers(with_super_admin_user), ) - assert response.status_code == 201 + assert response.status_code == 200 assert response.json["id"] == new_process_model_path # make sure the original model does not exist @@ -2595,7 +2595,7 @@ class TestProcessApi(BaseTest): f"/v1.0/process-groups/{modified_original_process_group_id}/move?new_location={new_location}", headers=self.logged_in_headers(with_super_admin_user), ) - assert response.status_code == 201 + assert response.status_code == 200 assert response.json["id"] == new_sub_path # make sure the original subgroup does not exist diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index a21baec0..06f7793c 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -79,6 +79,7 @@ type OwnProps = { textToShowIfEmpty?: string; paginationClassName?: string; autoReload?: boolean; + additionalParams?: string; }; interface dateParameters { @@ -90,6 +91,7 @@ export default function ProcessInstanceListTable({ processModelFullIdentifier, paginationQueryParamPrefix, perPageOptions, + additionalParams, showReports = true, reportIdentifier, textToShowIfEmpty, @@ -253,6 +255,10 @@ export default function ProcessInstanceListTable({ } ); + if (additionalParams) { + queryParamString += `&${additionalParams}`; + } + HttpService.makeCallToBackend({ path: `/process-instances?${queryParamString}`, successCallback: setProcessInstancesFromResult, @@ -320,6 +326,7 @@ export default function ProcessInstanceListTable({ processModelFullIdentifier, perPageOptions, reportIdentifier, + additionalParams, ]); // This sets the filter data using the saved reports returned from the initial instance_list query. diff --git a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx index 1412635c..bf197823 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx @@ -35,7 +35,7 @@ export default function ProcessModelListTiles({ setProcessModels(result.results); }; // 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) { queryParams = `${queryParams}&process_group_identifier=${processGroup.id}`; } else { diff --git a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx index 3584a6d2..3e744ddc 100644 --- a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx +++ b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx @@ -58,7 +58,7 @@ import { Can } from '@casl/react'; import HttpService from '../services/HttpService'; import ButtonWithConfirmation from './ButtonWithConfirmation'; -import { makeid } from '../helpers'; +import { getBpmnProcessIdentifiers, makeid } from '../helpers'; import { useUriListForPermissions } from '../hooks/UriListForPermissions'; import { PermissionsToCheck, ProcessInstanceTask } from '../interfaces'; import { usePermissionFetcher } from '../hooks/PermissionService'; @@ -70,6 +70,7 @@ type OwnProps = { completedProcessInstanceTasks?: ProcessInstanceTask[] | null; saveDiagram?: (..._args: any[]) => any; onDeleteFile?: (..._args: any[]) => any; + isPrimaryFile?: boolean; onSetPrimaryFile?: (..._args: any[]) => any; diagramXML?: string | null; fileName?: string; @@ -94,6 +95,7 @@ export default function ReactDiagramEditor({ completedProcessInstanceTasks, saveDiagram, onDeleteFile, + isPrimaryFile, onSetPrimaryFile, diagramXML, fileName, @@ -231,8 +233,10 @@ export default function ReactDiagramEditor({ function handleElementClick(event: any) { if (onElementClick) { const canvas = diagramModeler.get('canvas'); - const rootElement = canvas.getRootElement(); - onElementClick(event.element, rootElement); + const bpmnProcessIdentifiers = getBpmnProcessIdentifiers( + canvas.getRootElement() + ); + onElementClick(event.element, bpmnProcessIdentifiers); } } @@ -357,11 +361,15 @@ export default function ReactDiagramEditor({ canvas: any, processInstanceTask: ProcessInstanceTask, bpmnIoClassName: string, - bpmnRootElementId: string + bpmnProcessIdentifiers: string[] ) { if (checkTaskCanBeHighlighted(processInstanceTask.name)) { try { - if (bpmnRootElementId === processInstanceTask.process_identifier) { + if ( + bpmnProcessIdentifiers.includes( + processInstanceTask.process_identifier + ) + ) { canvas.addMarker(processInstanceTask.name, bpmnIoClassName); } } catch (bpmnIoError: any) { @@ -403,24 +411,28 @@ export default function ReactDiagramEditor({ // Option 3 at: // https://github.com/bpmn-io/bpmn-js-examples/tree/master/colors if (readyOrWaitingProcessInstanceTasks) { - const rootElement = canvas.getRootElement(); + const bpmnProcessIdentifiers = getBpmnProcessIdentifiers( + canvas.getRootElement() + ); readyOrWaitingProcessInstanceTasks.forEach((readyOrWaitingBpmnTask) => { highlightBpmnIoElement( canvas, readyOrWaitingBpmnTask, 'active-task-highlight', - rootElement.id + bpmnProcessIdentifiers ); }); } if (completedProcessInstanceTasks) { - const rootElement = canvas.getRootElement(); + const bpmnProcessIdentifiers = getBpmnProcessIdentifiers( + canvas.getRootElement() + ); completedProcessInstanceTasks.forEach((completedTask) => { highlightBpmnIoElement( canvas, completedTask, 'completed-task-highlight', - rootElement.id + bpmnProcessIdentifiers ); }); } @@ -563,7 +575,7 @@ export default function ReactDiagramEditor({ a={targetUris.processModelFileShowPath} ability={ability} > - {fileName && ( + {fileName && !isPrimaryFile && ( (null); + const [pagination, setPagination] = useState(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 ( + + + + {rowToUse.process_instance_id} + + + + + {rowToUse.process_model_display_name} + + + + {rowToUse.task_title} + + {showStartedBy ? {rowToUse.username} : ''} + {showWaitingOn ? {rowToUse.group_identifier || '-'} : ''} + + {convertSecondsToFormattedDateTime( + rowToUse.created_at_in_seconds + ) || '-'} + + + + + + + ); + }); + 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 ( + + + + {tableHeaders.map((tableHeader: string) => { + return ; + })} + + + {rows} +
{tableHeader}
+ ); + }; + + const tasksComponent = () => { + if (pagination && pagination.total < 1) { + return ( +

+ {textToShowIfEmpty} +

+ ); + } + const { page, perPage } = getPageInfoFromSearchParams( + searchParams, + PER_PAGE_FOR_TASKS_ON_HOME_PAGE, + undefined, + paginationQueryParamPrefix + ); + return ( + + ); + }; + + if (tasks) { + return ( + <> +

{tableTitle}

+

{tableDescription}

+ {tasksComponent()} + + ); + } + return null; +} diff --git a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx index 297f2071..be1d9042 100644 --- a/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx +++ b/spiffworkflow-frontend/src/components/TasksForMyOpenProcesses.tsx @@ -1,156 +1,18 @@ -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 } from '../interfaces'; -import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords'; +import TaskListTable from './TaskListTable'; -const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; const paginationQueryParamPrefix = 'tasks_for_my_open_processes'; -const REFRESH_INTERVAL = 5; -const REFRESH_TIMEOUT = 600; export default function MyOpenProcesses() { - const [searchParams] = useSearchParams(); - const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(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 ( - - - - {rowToUse.process_instance_id} - - - - - {rowToUse.process_model_display_name} - - - - {rowToUse.task_title} - - {rowToUse.group_identifier || '-'} - - {convertSecondsToFormattedDateTime( - rowToUse.created_at_in_seconds - ) || '-'} - - - - - - - ); - }); - return ( - - - - - - - - - - - - - {rows} -
IdProcessTaskWaiting ForDate StartedLast UpdatedActions
- ); - }; - - const tasksComponent = () => { - if (pagination && pagination.total < 1) { - return ( -

- There are no tasks for processes you started at this time. -

- ); - } - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - paginationQueryParamPrefix - ); - return ( - - ); - }; - return ( - <> -

My open instances

-

- 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. -

- {tasksComponent()} - + ); } diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx index 7d06b7a3..1939e4ba 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMe.tsx @@ -1,149 +1,16 @@ -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, -} from '../helpers'; -import HttpService from '../services/HttpService'; -import { PaginationObject } from '../interfaces'; -import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords'; - -const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; +import TaskListTable from './TaskListTable'; export default function TasksWaitingForMe() { - const [searchParams] = useSearchParams(); - const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(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 ( - - - - {rowToUse.process_instance_id} - - - - - {rowToUse.process_model_display_name} - - - - {rowToUse.task_title} - - {rowToUse.username} - {rowToUse.group_identifier || '-'} - - {convertSecondsToFormattedDateTime( - rowToUse.created_at_in_seconds - ) || '-'} - - - - - - - ); - }); - return ( - - - - - - - - - - - - - - {rows} -
IdProcessTaskStarted ByWaiting ForDate StartedLast UpdatedActions
- ); - }; - - const tasksComponent = () => { - if (pagination && pagination.total < 1) { - return ( -

- You have no task assignments at this time. -

- ); - } - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - 'tasks_waiting_for_me' - ); - return ( - - ); - }; - return ( - <> -

Tasks waiting for me

-

- These processes are waiting on you to complete the next task. All are - processes created by others that are now actionable by you. -

- {tasksComponent()} - + ); } diff --git a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx index 5b05dcd0..b7515c91 100644 --- a/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx +++ b/spiffworkflow-frontend/src/components/TasksWaitingForMyGroups.tsx @@ -1,156 +1,41 @@ 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 } from '../interfaces'; -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; +import TaskListTable from './TaskListTable'; export default function TasksWaitingForMyGroups() { - const [searchParams] = useSearchParams(); - const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(null); + const [userGroups, setUserGroups] = useState(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-groups?per_page=${perPage}&page=${page}`, - successCallback: setTasksFromResult, - }); - }; - getTasks(); - return refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks); - }, [searchParams]); + HttpService.makeCallToBackend({ + path: `/user-groups/for-current-user`, + successCallback: setUserGroups, + }); + }, [setUserGroups]); - 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); + const tableComponents = () => { + if (!userGroups) { + return null; + } + + return userGroups.map((userGroup: string) => { return ( - - - - {rowToUse.process_instance_id} - - - - - {rowToUse.process_model_display_name} - - - - {rowToUse.task_title} - - {rowToUse.username} - {rowToUse.group_identifier || '-'} - - {convertSecondsToFormattedDateTime( - rowToUse.created_at_in_seconds - ) || '-'} - - - - - - + ); }); - return ( - - - - - - - - - - - - - - {rows} -
IdProcessTaskStarted ByWaiting ForDate StartedLast UpdatedActions
- ); }; - const tasksComponent = () => { - if (pagination && pagination.total < 1) { - return ( -

- Your groups have no task assignments at this time. -

- ); - } - const { page, perPage } = getPageInfoFromSearchParams( - searchParams, - PER_PAGE_FOR_TASKS_ON_HOME_PAGE, - undefined, - paginationQueryParamPrefix - ); - return ( - - ); - }; - - return ( - <> -

Tasks waiting for my groups

-

- This is a list of tasks for groups you belong to that can be completed - by any member of the group. -

- {tasksComponent()} - - ); + if (userGroups) { + return <>{tableComponents()}; + } + return null; } diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index 0b73e517..8f625533 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -213,3 +213,24 @@ export const refreshAtInterval = ( clearTimeout(timeoutRef); }; }; + +const getChildProcesses = (bpmnElement: any) => { + let elements: string[] = []; + bpmnElement.children.forEach((c: any) => { + if (c.type === 'bpmn:Participant') { + if (c.businessObject.processRef) { + elements.push(c.businessObject.processRef.id); + } + elements = [...elements, ...getChildProcesses(c)]; + } else if (c.type === 'bpmn:SubProcess') { + elements.push(c.id); + } + }); + return elements; +}; + +export const getBpmnProcessIdentifiers = (rootBpmnElement: any) => { + const childProcesses = getChildProcesses(rootBpmnElement); + childProcesses.push(rootBpmnElement.businessObject.id); + return childProcesses; +}; diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx index f84465c8..4ba04352 100644 --- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx +++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx @@ -14,7 +14,8 @@ export const useUriListForPermissions = () => { processInstanceListPath: '/v1.0/process-instances', processInstanceLogListPath: `/v1.0/logs/${params.process_model_id}/${params.process_instance_id}`, 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}`, processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`, processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`, diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 6afb1144..b0ab6208 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -13,6 +13,12 @@ export interface RecentProcessModel { export interface ProcessInstanceTask { 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; process_identifier: string; name: string; diff --git a/spiffworkflow-frontend/src/routes/CompletedInstances.tsx b/spiffworkflow-frontend/src/routes/CompletedInstances.tsx index f97bb5d5..361692b0 100644 --- a/spiffworkflow-frontend/src/routes/CompletedInstances.tsx +++ b/spiffworkflow-frontend/src/routes/CompletedInstances.tsx @@ -1,6 +1,45 @@ +import { useEffect, useState } from 'react'; import ProcessInstanceListTable from '../components/ProcessInstanceListTable'; +import HttpService from '../services/HttpService'; export default function CompletedInstances() { + const [userGroups, setUserGroups] = useState(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 ( + <> +

With tasks completed by group: {userGroup}

+

+ This is a list of instances with tasks that were completed by the{' '} + {userGroup} group. +

+ + + ); + }); + }; + return ( <>

My completed instances

@@ -17,7 +56,7 @@ export default function CompletedInstances() { paginationClassName="with-large-bottom-margin" autoReload /> -

Tasks completed by me

+

With tasks completed by me

This is a list of instances where you have completed tasks.

@@ -27,22 +66,10 @@ export default function CompletedInstances() { perPageOptions={[2, 5, 25]} reportIdentifier="system_report_instances_with_tasks_completed_by_me" 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" /> -

Tasks completed by my groups

-

- This is a list of instances with tasks that were completed by groups you - belong to. -

- + {groupTableComponents()} ); } diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 3e0c2094..1adb585b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -70,8 +70,10 @@ export default function ProcessInstanceShow() { const permissionRequestData: PermissionsToCheck = { [targetUris.messageInstanceListPath]: ['GET'], [targetUris.processInstanceTaskListPath]: ['GET'], + [targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'], [targetUris.processInstanceActionPath]: ['DELETE'], [targetUris.processInstanceLogListPath]: ['GET'], + [targetUris.processModelShowPath]: ['PUT'], [`${targetUris.processInstanceActionPath}/suspend`]: ['PUT'], [`${targetUris.processInstanceActionPath}/terminate`]: ['PUT'], [`${targetUris.processInstanceActionPath}/resume`]: ['PUT'], @@ -104,9 +106,15 @@ export default function ProcessInstanceShow() { if (typeof params.spiff_step !== 'undefined') { 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({ - path: `${targetUris.processInstanceTaskListPath}${taskParams}`, + path: taskPath, successCallback: setTasks, failureCallback: processTaskFailure, }); @@ -392,13 +400,13 @@ export default function ProcessInstanceShow() { const handleClickedDiagramTask = ( shapeElement: any, - bpmnRootElement: any + bpmnProcessIdentifiers: any ) => { if (tasks) { const matchingTask: any = tasks.find( (task: any) => task.name === shapeElement.id && - task.process_identifier === bpmnRootElement.id + bpmnProcessIdentifiers.includes(task.process_identifier) ); if (matchingTask) { setTaskToDisplay(matchingTask); @@ -442,7 +450,9 @@ export default function ProcessInstanceShow() { const canEditTaskData = (task: any) => { return ( - task.state === 'READY' && showingLastSpiffStep(processInstance as any) + ability.can('PUT', targetUris.processInstanceTaskListDataPath) && + task.state === 'READY' && + showingLastSpiffStep(processInstance as any) ); }; @@ -491,7 +501,10 @@ export default function ProcessInstanceShow() { const taskDataButtons = (task: any) => { const buttons = []; - if (task.type === 'Script Task') { + if ( + task.type === 'Script Task' && + ability.can('PUT', targetUris.processModelShowPath) + ) { buttons.push(