mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-02-27 00:40:37 +00:00
Merge branch 'main' into feature/create_containers
This commit is contained in:
commit
d80e4b3456
@ -1,5 +1,5 @@
|
|||||||
pip==22.2.2
|
pip==22.2.2
|
||||||
nox==2022.8.7
|
nox==2022.11.21
|
||||||
nox-poetry==1.0.1
|
nox-poetry==1.0.2
|
||||||
poetry==1.2.2
|
poetry==1.2.2
|
||||||
virtualenv==20.16.5
|
virtualenv==20.16.5
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"""empty message
|
"""empty message
|
||||||
|
|
||||||
Revision ID: 3f049fa4d8ac
|
Revision ID: 4d75421c0af0
|
||||||
Revises:
|
Revises:
|
||||||
Create Date: 2022-11-30 16:49:54.805372
|
Create Date: 2022-12-06 17:42:56.417673
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from alembic import op
|
from alembic import op
|
||||||
@ -10,7 +10,7 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = '3f049fa4d8ac'
|
revision = '4d75421c0af0'
|
||||||
down_revision = None
|
down_revision = None
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
@ -248,6 +248,7 @@ def upgrade():
|
|||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint('id'),
|
||||||
sa.UniqueConstraint('process_instance_id', 'key', name='process_instance_metadata_unique')
|
sa.UniqueConstraint('process_instance_id', 'key', name='process_instance_metadata_unique')
|
||||||
)
|
)
|
||||||
|
op.create_index(op.f('ix_process_instance_metadata_key'), 'process_instance_metadata', ['key'], unique=False)
|
||||||
op.create_table('spiff_step_details',
|
op.create_table('spiff_step_details',
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
sa.Column('process_instance_id', sa.Integer(), nullable=False),
|
sa.Column('process_instance_id', sa.Integer(), nullable=False),
|
||||||
@ -294,6 +295,7 @@ def downgrade():
|
|||||||
op.drop_index(op.f('ix_active_task_user_active_task_id'), table_name='active_task_user')
|
op.drop_index(op.f('ix_active_task_user_active_task_id'), table_name='active_task_user')
|
||||||
op.drop_table('active_task_user')
|
op.drop_table('active_task_user')
|
||||||
op.drop_table('spiff_step_details')
|
op.drop_table('spiff_step_details')
|
||||||
|
op.drop_index(op.f('ix_process_instance_metadata_key'), table_name='process_instance_metadata')
|
||||||
op.drop_table('process_instance_metadata')
|
op.drop_table('process_instance_metadata')
|
||||||
op.drop_table('permission_assignment')
|
op.drop_table('permission_assignment')
|
||||||
op.drop_table('message_instance')
|
op.drop_table('message_instance')
|
13
spiffworkflow-backend/poetry.lock
generated
13
spiffworkflow-backend/poetry.lock
generated
@ -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 = "062eaf15d28c66f8cf07f68409429560251b12c7"
|
resolved_reference = "ffb1686757f944065580dd2db8def73d6c1f0134"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "SQLAlchemy"
|
name = "SQLAlchemy"
|
||||||
@ -2989,7 +2989,18 @@ 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 = [
|
||||||
|
@ -108,11 +108,14 @@ def create_app() -> flask.app.Flask:
|
|||||||
app.register_blueprint(admin_blueprint, url_prefix="/admin")
|
app.register_blueprint(admin_blueprint, url_prefix="/admin")
|
||||||
app.register_blueprint(openid_blueprint, url_prefix="/openid")
|
app.register_blueprint(openid_blueprint, url_prefix="/openid")
|
||||||
|
|
||||||
|
# preflight options requests will be allowed if they meet the requirements of the url regex.
|
||||||
|
# we will add an Access-Control-Max-Age header to the response to tell the browser it doesn't
|
||||||
|
# need to continually keep asking for the same path.
|
||||||
origins_re = [
|
origins_re = [
|
||||||
r"^https?:\/\/%s(.*)" % o.replace(".", r"\.")
|
r"^https?:\/\/%s(.*)" % o.replace(".", r"\.")
|
||||||
for o in app.config["CORS_ALLOW_ORIGINS"]
|
for o in app.config["CORS_ALLOW_ORIGINS"]
|
||||||
]
|
]
|
||||||
CORS(app, origins=origins_re)
|
CORS(app, origins=origins_re, max_age=3600)
|
||||||
|
|
||||||
connexion_app.add_api("api.yml", base_path="/v1.0")
|
connexion_app.add_api("api.yml", base_path="/v1.0")
|
||||||
|
|
||||||
|
@ -338,9 +338,9 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/ProcessModel"
|
$ref: "#/components/schemas/ProcessModel"
|
||||||
|
|
||||||
/process-models/{modified_process_model_id}/files:
|
/process-models/{modified_process_model_identifier}/files:
|
||||||
parameters:
|
parameters:
|
||||||
- name: modified_process_model_id
|
- name: modified_process_model_identifier
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: The process_model_id, modified to replace slashes (/)
|
description: The process_model_id, modified to replace slashes (/)
|
||||||
@ -544,6 +544,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: string
|
type: string
|
||||||
|
- name: report_id
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: Specifies the identifier of a report to use, if any
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
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
|
||||||
@ -559,33 +565,6 @@ paths:
|
|||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Workflow"
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
/process-instances/{process_instance_id}/task/{task_id}/update:
|
|
||||||
parameters:
|
|
||||||
- name: process_instance_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
description: The unique id of the process instance
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- name: task_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
description: The unique id of the task
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
post:
|
|
||||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.update_task_data
|
|
||||||
summary: Update the task data for requested instance and task
|
|
||||||
tags:
|
|
||||||
- Process Instances
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Task Updated Successfully
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: "#/components/schemas/Workflow"
|
|
||||||
|
|
||||||
/process-models/{process_group_id}/{process_model_id}/script-unit-tests:
|
/process-models/{process_group_id}/{process_model_id}/script-unit-tests:
|
||||||
parameters:
|
parameters:
|
||||||
- name: process_group_id
|
- name: process_group_id
|
||||||
@ -640,15 +619,14 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Workflow"
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
/process-models/{modified_process_model_id}/process-instances:
|
/process-instances/{modified_process_model_identifier}:
|
||||||
parameters:
|
parameters:
|
||||||
- name: modified_process_model_id
|
- name: modified_process_model_identifier
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: The unique id of an existing process model.
|
description: The unique id of an existing process model.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
# process_instance_create
|
|
||||||
post:
|
post:
|
||||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_create
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_create
|
||||||
summary: Creates an process instance from a process model and returns the instance
|
summary: Creates an process instance from a process model and returns the instance
|
||||||
@ -662,28 +640,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Workflow"
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
/process-instances/{process_instance_id}:
|
/process-instances/{modified_process_model_identifier}/{process_instance_id}:
|
||||||
parameters:
|
|
||||||
- name: process_instance_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
description: The unique id of an existing process instance.
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
delete:
|
|
||||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_delete
|
|
||||||
summary: Deletes a single process instance
|
|
||||||
tags:
|
|
||||||
- Process Instances
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: The process instance was deleted.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: "#/components/schemas/OkTrue"
|
|
||||||
|
|
||||||
/process-models/{modified_process_model_identifier}/process-instances/{process_instance_id}:
|
|
||||||
parameters:
|
parameters:
|
||||||
- name: modified_process_model_identifier
|
- name: modified_process_model_identifier
|
||||||
in: path
|
in: path
|
||||||
@ -709,6 +666,18 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Workflow"
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
delete:
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_delete
|
||||||
|
summary: Deletes a single process instance
|
||||||
|
tags:
|
||||||
|
- Process Instances
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The process instance was deleted.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/OkTrue"
|
||||||
|
|
||||||
/process-instances/{modified_process_model_identifier}/{process_instance_id}/run:
|
/process-instances/{modified_process_model_identifier}/{process_instance_id}/run:
|
||||||
parameters:
|
parameters:
|
||||||
@ -737,7 +706,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Workflow"
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
/process-instances/{process_instance_id}/terminate:
|
/process-instances/{modified_process_model_identifier}/{process_instance_id}/terminate:
|
||||||
parameters:
|
parameters:
|
||||||
- name: process_instance_id
|
- name: process_instance_id
|
||||||
in: path
|
in: path
|
||||||
@ -758,7 +727,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/OkTrue"
|
$ref: "#/components/schemas/OkTrue"
|
||||||
|
|
||||||
/process-instances/{process_instance_id}/suspend:
|
/process-instances/{modified_process_model_identifier}/{process_instance_id}/suspend:
|
||||||
parameters:
|
parameters:
|
||||||
- name: process_instance_id
|
- name: process_instance_id
|
||||||
in: path
|
in: path
|
||||||
@ -779,7 +748,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/OkTrue"
|
$ref: "#/components/schemas/OkTrue"
|
||||||
|
|
||||||
/process-instances/{process_instance_id}/resume:
|
/process-instances/{modified_process_model_identifier}/{process_instance_id}/resume:
|
||||||
parameters:
|
parameters:
|
||||||
- name: process_instance_id
|
- name: process_instance_id
|
||||||
in: path
|
in: path
|
||||||
@ -841,14 +810,30 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/OkTrue"
|
$ref: "#/components/schemas/OkTrue"
|
||||||
|
|
||||||
/process-instances/reports/{report_identifier}:
|
/process-instances/reports/columns:
|
||||||
|
get:
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_report_column_list
|
||||||
|
summary: Returns all available columns for a process instance report.
|
||||||
|
tags:
|
||||||
|
- Process Instances
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Workflow.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
|
/process-instances/reports/{report_id}:
|
||||||
parameters:
|
parameters:
|
||||||
- name: report_identifier
|
- name: report_id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: The unique id of an existing report
|
description: The unique id of an existing report
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: integer
|
||||||
- name: page
|
- name: page
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
@ -900,9 +885,9 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/OkTrue"
|
$ref: "#/components/schemas/OkTrue"
|
||||||
|
|
||||||
/process-models/{modified_process_model_id}/files/{file_name}:
|
/process-models/{modified_process_model_identifier}/files/{file_name}:
|
||||||
parameters:
|
parameters:
|
||||||
- name: modified_process_model_id
|
- name: modified_process_model_identifier
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: The modified process model id
|
description: The modified process model id
|
||||||
@ -1079,9 +1064,9 @@ paths:
|
|||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Task"
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
/process-instances/{modified_process_model_id}/{process_instance_id}/tasks:
|
/task-data/{modified_process_model_identifier}/{process_instance_id}:
|
||||||
parameters:
|
parameters:
|
||||||
- name: modified_process_model_id
|
- name: modified_process_model_identifier
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: The modified id of an existing process model
|
description: The modified id of an existing process model
|
||||||
@ -1120,11 +1105,44 @@ paths:
|
|||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Task"
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
/service_tasks:
|
/task-data/{modified_process_model_identifier}/{process_instance_id}/{task_id}:
|
||||||
|
parameters:
|
||||||
|
- name: modified_process_model_identifier
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The modified 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: task_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The unique id of the task.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
put:
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.update_task_data
|
||||||
|
summary: Update the task data for requested instance and task
|
||||||
|
tags:
|
||||||
|
- Process Instances
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Task Updated Successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
|
/service-tasks:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Service Tasks
|
- Service Tasks
|
||||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.service_tasks_show
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.service_task_list
|
||||||
summary: Gets all available service task connectors
|
summary: Gets all available service task connectors
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -1304,7 +1322,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Workflow"
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
/process-instances/{process_instance_id}/logs:
|
/logs/{modified_process_model_identifier}/{process_instance_id}:
|
||||||
parameters:
|
parameters:
|
||||||
- name: process_instance_id
|
- name: process_instance_id
|
||||||
in: path
|
in: path
|
||||||
@ -1324,6 +1342,12 @@ paths:
|
|||||||
description: The number of items to show per page. Defaults to page 10.
|
description: The number of items to show per page. Defaults to page 10.
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
|
- name: detailed
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: Show the detailed view, which includes all log entries
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Process Instances
|
- Process Instances
|
||||||
|
@ -18,7 +18,6 @@ groups:
|
|||||||
mike,
|
mike,
|
||||||
jason,
|
jason,
|
||||||
j,
|
j,
|
||||||
amir,
|
|
||||||
jarrad,
|
jarrad,
|
||||||
elizabeth,
|
elizabeth,
|
||||||
jon,
|
jon,
|
||||||
@ -76,6 +75,12 @@ permissions:
|
|||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/tasks/*
|
uri: /v1.0/tasks/*
|
||||||
|
service-tasks:
|
||||||
|
groups: [everybody]
|
||||||
|
users: []
|
||||||
|
allowed_permissions: [read]
|
||||||
|
uri: /v1.0/service-tasks
|
||||||
|
|
||||||
|
|
||||||
# read all for everybody
|
# read all for everybody
|
||||||
read-all-process-groups:
|
read-all-process-groups:
|
||||||
@ -104,6 +109,12 @@ 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"]
|
||||||
@ -176,17 +187,17 @@ permissions:
|
|||||||
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:*
|
||||||
|
|
||||||
core1-admin-models-instantiate:
|
core1-admin-models-instantiate:
|
||||||
groups: ["core-contributor"]
|
groups: ["core-contributor", "Finance Team"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create]
|
allowed_permissions: [create]
|
||||||
uri: /v1.0/process-models/misc:category_number_one:process-model-with-form/process-instances
|
uri: /v1.0/process-models/misc:category_number_one:process-model-with-form/process-instances
|
||||||
core1-admin-instances:
|
core1-admin-instances:
|
||||||
groups: ["core-contributor"]
|
groups: ["core-contributor", "Finance Team"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create, read]
|
||||||
uri: /v1.0/process-instances/misc:category_number_one:process-model-with-form:*
|
uri: /v1.0/process-instances/misc:category_number_one:process-model-with-form:*
|
||||||
core1-admin-instances-slash:
|
core1-admin-instances-slash:
|
||||||
groups: ["core-contributor"]
|
groups: ["core-contributor", "Finance Team"]
|
||||||
users: []
|
users: []
|
||||||
allowed_permissions: [create, read]
|
allowed_permissions: [create, read]
|
||||||
uri: /v1.0/process-instances/misc:category_number_one:process-model-with-form/*
|
uri: /v1.0/process-instances/misc:category_number_one:process-model-with-form/*
|
||||||
|
@ -12,7 +12,6 @@ groups:
|
|||||||
mike,
|
mike,
|
||||||
jason,
|
jason,
|
||||||
j,
|
j,
|
||||||
amir,
|
|
||||||
jarrad,
|
jarrad,
|
||||||
elizabeth,
|
elizabeth,
|
||||||
jon,
|
jon,
|
||||||
@ -71,6 +70,13 @@ permissions:
|
|||||||
allowed_permissions: [create, read, update, delete]
|
allowed_permissions: [create, read, update, delete]
|
||||||
uri: /v1.0/tasks/*
|
uri: /v1.0/tasks/*
|
||||||
|
|
||||||
|
service-tasks:
|
||||||
|
groups: [everybody]
|
||||||
|
users: []
|
||||||
|
allowed_permissions: [read]
|
||||||
|
uri: /v1.0/service-tasks
|
||||||
|
|
||||||
|
|
||||||
# read all for everybody
|
# read all for everybody
|
||||||
read-all-process-groups:
|
read-all-process-groups:
|
||||||
groups: [everybody]
|
groups: [everybody]
|
||||||
@ -98,6 +104,12 @@ 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"]
|
||||||
|
@ -23,7 +23,7 @@ class ProcessInstanceMetadataModel(SpiffworkflowBaseDBModel):
|
|||||||
process_instance_id: int = db.Column(
|
process_instance_id: int = db.Column(
|
||||||
ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore
|
ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore
|
||||||
)
|
)
|
||||||
key: str = db.Column(db.String(255), nullable=False)
|
key: str = db.Column(db.String(255), nullable=False, index=True)
|
||||||
value: str = db.Column(db.String(255), nullable=False)
|
value: str = db.Column(db.String(255), nullable=False)
|
||||||
|
|
||||||
updated_at_in_seconds: int = db.Column(db.Integer, nullable=False)
|
updated_at_in_seconds: int = db.Column(db.Integer, nullable=False)
|
||||||
|
@ -26,6 +26,10 @@ from spiffworkflow_backend.services.process_instance_processor import (
|
|||||||
ReportMetadata = dict[str, Any]
|
ReportMetadata = dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessInstanceReportAlreadyExistsError(Exception):
|
||||||
|
"""ProcessInstanceReportAlreadyExistsError."""
|
||||||
|
|
||||||
|
|
||||||
class ProcessInstanceReportResult(TypedDict):
|
class ProcessInstanceReportResult(TypedDict):
|
||||||
"""ProcessInstanceReportResult."""
|
"""ProcessInstanceReportResult."""
|
||||||
|
|
||||||
@ -63,7 +67,7 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id: int = db.Column(db.Integer, primary_key=True)
|
||||||
identifier: str = db.Column(db.String(50), nullable=False, index=True)
|
identifier: str = db.Column(db.String(50), nullable=False, index=True)
|
||||||
report_metadata: dict = deferred(db.Column(db.JSON)) # type: ignore
|
report_metadata: dict = deferred(db.Column(db.JSON)) # type: ignore
|
||||||
created_by_id = db.Column(ForeignKey(UserModel.id), nullable=False, index=True)
|
created_by_id = db.Column(ForeignKey(UserModel.id), nullable=False, index=True)
|
||||||
@ -71,6 +75,11 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
|
|||||||
created_at_in_seconds = db.Column(db.Integer)
|
created_at_in_seconds = db.Column(db.Integer)
|
||||||
updated_at_in_seconds = db.Column(db.Integer)
|
updated_at_in_seconds = db.Column(db.Integer)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_order_by(cls) -> list[str]:
|
||||||
|
"""Default_order_by."""
|
||||||
|
return ["-start_in_seconds", "-id"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_fixtures(cls) -> None:
|
def add_fixtures(cls) -> None:
|
||||||
"""Add_fixtures."""
|
"""Add_fixtures."""
|
||||||
@ -120,21 +129,27 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
|
|||||||
identifier: str,
|
identifier: str,
|
||||||
user: UserModel,
|
user: UserModel,
|
||||||
report_metadata: ReportMetadata,
|
report_metadata: ReportMetadata,
|
||||||
) -> None:
|
) -> ProcessInstanceReportModel:
|
||||||
"""Make_fixture_report."""
|
"""Make_fixture_report."""
|
||||||
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
created_by_id=user.id,
|
created_by_id=user.id,
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if process_instance_report is None:
|
if process_instance_report is not None:
|
||||||
process_instance_report = cls(
|
raise ProcessInstanceReportAlreadyExistsError(
|
||||||
identifier=identifier,
|
f"Process instance report with identifier already exists: {identifier}"
|
||||||
created_by_id=user.id,
|
|
||||||
report_metadata=report_metadata,
|
|
||||||
)
|
)
|
||||||
db.session.add(process_instance_report)
|
|
||||||
db.session.commit()
|
process_instance_report = cls(
|
||||||
|
identifier=identifier,
|
||||||
|
created_by_id=user.id,
|
||||||
|
report_metadata=report_metadata,
|
||||||
|
)
|
||||||
|
db.session.add(process_instance_report)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return process_instance_report # type: ignore
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ticket_for_month_report(cls) -> dict:
|
def ticket_for_month_report(cls) -> dict:
|
||||||
@ -204,18 +219,8 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
|
|||||||
user: UserModel,
|
user: UserModel,
|
||||||
) -> ProcessInstanceReportModel:
|
) -> ProcessInstanceReportModel:
|
||||||
"""Create_with_attributes."""
|
"""Create_with_attributes."""
|
||||||
# <<<<<<< HEAD
|
|
||||||
# process_model = ProcessModelService.get_process_model(
|
|
||||||
# process_model_id=f"{process_model_identifier}"
|
|
||||||
# )
|
|
||||||
# process_instance_report = cls(
|
|
||||||
# identifier=identifier,
|
|
||||||
# process_group_identifier="process_model.process_group_id",
|
|
||||||
# process_model_identifier=process_model.id,
|
|
||||||
# =======
|
|
||||||
process_instance_report = cls(
|
process_instance_report = cls(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
# >>>>>>> main
|
|
||||||
created_by_id=user.id,
|
created_by_id=user.id,
|
||||||
report_metadata=report_metadata,
|
report_metadata=report_metadata,
|
||||||
)
|
)
|
||||||
|
@ -38,6 +38,7 @@ class ProcessModelInfo:
|
|||||||
fault_or_suspend_on_exception: str = NotificationType.fault.value
|
fault_or_suspend_on_exception: str = NotificationType.fault.value
|
||||||
exception_notification_addresses: list[str] = field(default_factory=list)
|
exception_notification_addresses: list[str] = field(default_factory=list)
|
||||||
parent_groups: list[dict] | None = None
|
parent_groups: list[dict] | None = None
|
||||||
|
metadata_extraction_paths: list[dict[str, str]] | None = None
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
"""__post_init__."""
|
"""__post_init__."""
|
||||||
@ -76,6 +77,13 @@ class ProcessModelInfoSchema(Schema):
|
|||||||
exception_notification_addresses = marshmallow.fields.List(
|
exception_notification_addresses = marshmallow.fields.List(
|
||||||
marshmallow.fields.String
|
marshmallow.fields.String
|
||||||
)
|
)
|
||||||
|
metadata_extraction_paths = marshmallow.fields.List(
|
||||||
|
marshmallow.fields.Dict(
|
||||||
|
keys=marshmallow.fields.Str(required=False),
|
||||||
|
values=marshmallow.fields.Str(required=False),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@post_load
|
@post_load
|
||||||
def make_spec(
|
def make_spec(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""APIs for dealing with process groups, process models, and process instances."""
|
"""APIs for dealing with process groups, process models, and process instances."""
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
import uuid
|
import uuid
|
||||||
from flask_bpmn.api.api_error import ApiError
|
from flask_bpmn.api.api_error import ApiError
|
||||||
@ -30,6 +31,9 @@ from SpiffWorkflow.task import TaskState
|
|||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
from sqlalchemy import asc
|
from sqlalchemy import asc
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
from sqlalchemy import func
|
||||||
|
from sqlalchemy.orm import aliased
|
||||||
|
from sqlalchemy.orm import joinedload
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
|
|
||||||
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
|
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
|
||||||
@ -52,6 +56,9 @@ from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSche
|
|||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
||||||
|
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||||
|
ProcessInstanceMetadataModel,
|
||||||
|
)
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
from spiffworkflow_backend.models.process_instance_report import (
|
||||||
ProcessInstanceReportModel,
|
ProcessInstanceReportModel,
|
||||||
)
|
)
|
||||||
@ -152,9 +159,9 @@ def modify_process_model_id(process_model_id: str) -> str:
|
|||||||
return process_model_id.replace("/", ":")
|
return process_model_id.replace("/", ":")
|
||||||
|
|
||||||
|
|
||||||
def un_modify_modified_process_model_id(modified_process_model_id: str) -> str:
|
def un_modify_modified_process_model_id(modified_process_model_identifier: str) -> str:
|
||||||
"""Un_modify_modified_process_model_id."""
|
"""Un_modify_modified_process_model_id."""
|
||||||
return modified_process_model_id.replace(":", "/")
|
return modified_process_model_identifier.replace(":", "/")
|
||||||
|
|
||||||
|
|
||||||
def process_group_add(body: dict) -> flask.wrappers.Response:
|
def process_group_add(body: dict) -> flask.wrappers.Response:
|
||||||
@ -256,19 +263,26 @@ def process_model_create(
|
|||||||
modified_process_group_id: str, body: Dict[str, Union[str, bool, int]]
|
modified_process_group_id: str, body: Dict[str, Union[str, bool, int]]
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_model_create."""
|
"""Process_model_create."""
|
||||||
process_model_info = ProcessModelInfoSchema().load(body)
|
body_include_list = [
|
||||||
|
"id",
|
||||||
|
"display_name",
|
||||||
|
"primary_file_name",
|
||||||
|
"primary_process_id",
|
||||||
|
"description",
|
||||||
|
"metadata_extraction_paths",
|
||||||
|
]
|
||||||
|
body_filtered = {
|
||||||
|
include_item: body[include_item]
|
||||||
|
for include_item in body_include_list
|
||||||
|
if include_item in body
|
||||||
|
}
|
||||||
|
|
||||||
if modified_process_group_id is None:
|
if modified_process_group_id is None:
|
||||||
raise ApiError(
|
raise ApiError(
|
||||||
error_code="process_group_id_not_specified",
|
error_code="process_group_id_not_specified",
|
||||||
message="Process Model could not be created when process_group_id path param is unspecified",
|
message="Process Model could not be created when process_group_id path param is unspecified",
|
||||||
status_code=400,
|
status_code=400,
|
||||||
)
|
)
|
||||||
if process_model_info is None:
|
|
||||||
raise ApiError(
|
|
||||||
error_code="process_model_could_not_be_created",
|
|
||||||
message=f"Process Model could not be created from given body: {body}",
|
|
||||||
status_code=400,
|
|
||||||
)
|
|
||||||
|
|
||||||
unmodified_process_group_id = un_modify_modified_process_model_id(
|
unmodified_process_group_id = un_modify_modified_process_model_id(
|
||||||
modified_process_group_id
|
modified_process_group_id
|
||||||
@ -281,6 +295,14 @@ def process_model_create(
|
|||||||
status_code=400,
|
status_code=400,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
process_model_info = ProcessModelInfo(**body_filtered) # type: ignore
|
||||||
|
if process_model_info is None:
|
||||||
|
raise ApiError(
|
||||||
|
error_code="process_model_could_not_be_created",
|
||||||
|
message=f"Process Model could not be created from given body: {body}",
|
||||||
|
status_code=400,
|
||||||
|
)
|
||||||
|
|
||||||
ProcessModelService.add_process_model(process_model_info)
|
ProcessModelService.add_process_model(process_model_info)
|
||||||
return Response(
|
return Response(
|
||||||
json.dumps(ProcessModelInfoSchema().dump(process_model_info)),
|
json.dumps(ProcessModelInfoSchema().dump(process_model_info)),
|
||||||
@ -294,7 +316,6 @@ def process_model_delete(
|
|||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_model_delete."""
|
"""Process_model_delete."""
|
||||||
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
||||||
# process_model_identifier = f"{process_group_id}/{process_model_id}"
|
|
||||||
ProcessModelService().process_model_delete(process_model_identifier)
|
ProcessModelService().process_model_delete(process_model_identifier)
|
||||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
||||||
|
|
||||||
@ -309,6 +330,7 @@ def process_model_update(
|
|||||||
"primary_file_name",
|
"primary_file_name",
|
||||||
"primary_process_id",
|
"primary_process_id",
|
||||||
"description",
|
"description",
|
||||||
|
"metadata_extraction_paths",
|
||||||
]
|
]
|
||||||
body_filtered = {
|
body_filtered = {
|
||||||
include_item: body[include_item]
|
include_item: body[include_item]
|
||||||
@ -316,7 +338,6 @@ def process_model_update(
|
|||||||
if include_item in body
|
if include_item in body
|
||||||
}
|
}
|
||||||
|
|
||||||
# process_model_identifier = f"{process_group_id}/{process_model_id}"
|
|
||||||
process_model = get_process_model(process_model_identifier)
|
process_model = get_process_model(process_model_identifier)
|
||||||
ProcessModelService.update_process_model(process_model, body_filtered)
|
ProcessModelService.update_process_model(process_model, body_filtered)
|
||||||
return ProcessModelInfoSchema().dump(process_model)
|
return ProcessModelInfoSchema().dump(process_model)
|
||||||
@ -325,10 +346,7 @@ def process_model_update(
|
|||||||
def process_model_show(modified_process_model_identifier: str) -> Any:
|
def process_model_show(modified_process_model_identifier: str) -> Any:
|
||||||
"""Process_model_show."""
|
"""Process_model_show."""
|
||||||
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
||||||
# process_model_identifier = f"{process_group_id}/{process_model_id}"
|
|
||||||
process_model = get_process_model(process_model_identifier)
|
process_model = get_process_model(process_model_identifier)
|
||||||
# TODO: Temporary. Should not need the next line once models have correct ids
|
|
||||||
# process_model.id = process_model_identifier
|
|
||||||
files = sorted(SpecFileService.get_files(process_model))
|
files = sorted(SpecFileService.get_files(process_model))
|
||||||
process_model.files = files
|
process_model.files = files
|
||||||
for file in process_model.files:
|
for file in process_model.files:
|
||||||
@ -394,9 +412,9 @@ def process_list() -> Any:
|
|||||||
return SpecReferenceSchema(many=True).dump(references)
|
return SpecReferenceSchema(many=True).dump(references)
|
||||||
|
|
||||||
|
|
||||||
def get_file(modified_process_model_id: str, file_name: str) -> Any:
|
def get_file(modified_process_model_identifier: str, file_name: str) -> Any:
|
||||||
"""Get_file."""
|
"""Get_file."""
|
||||||
process_model_identifier = modified_process_model_id.replace(":", "/")
|
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
||||||
process_model = get_process_model(process_model_identifier)
|
process_model = get_process_model(process_model_identifier)
|
||||||
files = SpecFileService.get_files(process_model, file_name)
|
files = SpecFileService.get_files(process_model, file_name)
|
||||||
if len(files) == 0:
|
if len(files) == 0:
|
||||||
@ -416,11 +434,10 @@ def get_file(modified_process_model_id: str, file_name: str) -> Any:
|
|||||||
|
|
||||||
|
|
||||||
def process_model_file_update(
|
def process_model_file_update(
|
||||||
modified_process_model_id: str, file_name: str
|
modified_process_model_identifier: str, file_name: str
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_model_file_update."""
|
"""Process_model_file_update."""
|
||||||
process_model_identifier = modified_process_model_id.replace(":", "/")
|
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
||||||
# process_model_identifier = f"{process_group_id}/{process_model_id}"
|
|
||||||
process_model = get_process_model(process_model_identifier)
|
process_model = get_process_model(process_model_identifier)
|
||||||
|
|
||||||
request_file = get_file_from_request()
|
request_file = get_file_from_request()
|
||||||
@ -446,10 +463,10 @@ def process_model_file_update(
|
|||||||
|
|
||||||
|
|
||||||
def process_model_file_delete(
|
def process_model_file_delete(
|
||||||
modified_process_model_id: str, file_name: str
|
modified_process_model_identifier: str, file_name: str
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_model_file_delete."""
|
"""Process_model_file_delete."""
|
||||||
process_model_identifier = modified_process_model_id.replace(":", "/")
|
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
||||||
process_model = get_process_model(process_model_identifier)
|
process_model = get_process_model(process_model_identifier)
|
||||||
try:
|
try:
|
||||||
SpecFileService.delete_file(process_model, file_name)
|
SpecFileService.delete_file(process_model, file_name)
|
||||||
@ -465,9 +482,9 @@ def process_model_file_delete(
|
|||||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
||||||
|
|
||||||
|
|
||||||
def add_file(modified_process_model_id: str) -> flask.wrappers.Response:
|
def add_file(modified_process_model_identifier: str) -> flask.wrappers.Response:
|
||||||
"""Add_file."""
|
"""Add_file."""
|
||||||
process_model_identifier = modified_process_model_id.replace(":", "/")
|
process_model_identifier = modified_process_model_identifier.replace(":", "/")
|
||||||
process_model = get_process_model(process_model_identifier)
|
process_model = get_process_model(process_model_identifier)
|
||||||
request_file = get_file_from_request()
|
request_file = get_file_from_request()
|
||||||
if not request_file.filename:
|
if not request_file.filename:
|
||||||
@ -488,10 +505,12 @@ def add_file(modified_process_model_id: str) -> flask.wrappers.Response:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def process_instance_create(modified_process_model_id: str) -> flask.wrappers.Response:
|
def process_instance_create(
|
||||||
|
modified_process_model_identifier: str,
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
"""Create_process_instance."""
|
"""Create_process_instance."""
|
||||||
process_model_identifier = un_modify_modified_process_model_id(
|
process_model_identifier = un_modify_modified_process_model_id(
|
||||||
modified_process_model_id
|
modified_process_model_identifier
|
||||||
)
|
)
|
||||||
process_instance = (
|
process_instance = (
|
||||||
ProcessInstanceService.create_process_instance_from_process_model_identifier(
|
ProcessInstanceService.create_process_instance_from_process_model_identifier(
|
||||||
@ -549,6 +568,7 @@ def process_instance_run(
|
|||||||
|
|
||||||
def process_instance_terminate(
|
def process_instance_terminate(
|
||||||
process_instance_id: int,
|
process_instance_id: int,
|
||||||
|
modified_process_model_identifier: str,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_run."""
|
"""Process_instance_run."""
|
||||||
process_instance = ProcessInstanceService().get_process_instance(
|
process_instance = ProcessInstanceService().get_process_instance(
|
||||||
@ -561,6 +581,7 @@ def process_instance_terminate(
|
|||||||
|
|
||||||
def process_instance_suspend(
|
def process_instance_suspend(
|
||||||
process_instance_id: int,
|
process_instance_id: int,
|
||||||
|
modified_process_model_identifier: str,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_suspend."""
|
"""Process_instance_suspend."""
|
||||||
process_instance = ProcessInstanceService().get_process_instance(
|
process_instance = ProcessInstanceService().get_process_instance(
|
||||||
@ -573,6 +594,7 @@ def process_instance_suspend(
|
|||||||
|
|
||||||
def process_instance_resume(
|
def process_instance_resume(
|
||||||
process_instance_id: int,
|
process_instance_id: int,
|
||||||
|
modified_process_model_identifier: str,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_resume."""
|
"""Process_instance_resume."""
|
||||||
process_instance = ProcessInstanceService().get_process_instance(
|
process_instance = ProcessInstanceService().get_process_instance(
|
||||||
@ -584,19 +606,24 @@ def process_instance_resume(
|
|||||||
|
|
||||||
|
|
||||||
def process_instance_log_list(
|
def process_instance_log_list(
|
||||||
|
modified_process_model_identifier: str,
|
||||||
process_instance_id: int,
|
process_instance_id: int,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
per_page: int = 100,
|
per_page: int = 100,
|
||||||
|
detailed: bool = False,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_log_list."""
|
"""Process_instance_log_list."""
|
||||||
# to make sure the process instance exists
|
# to make sure the process instance exists
|
||||||
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
||||||
|
|
||||||
|
log_query = SpiffLoggingModel.query.filter(
|
||||||
|
SpiffLoggingModel.process_instance_id == process_instance.id
|
||||||
|
)
|
||||||
|
if not detailed:
|
||||||
|
log_query = log_query.filter(SpiffLoggingModel.message.in_(["State change to COMPLETED"])) # type: ignore
|
||||||
|
|
||||||
logs = (
|
logs = (
|
||||||
SpiffLoggingModel.query.filter(
|
log_query.order_by(SpiffLoggingModel.timestamp.desc()) # type: ignore
|
||||||
SpiffLoggingModel.process_instance_id == process_instance.id
|
|
||||||
)
|
|
||||||
.order_by(SpiffLoggingModel.timestamp.desc()) # type: ignore
|
|
||||||
.join(
|
.join(
|
||||||
UserModel, UserModel.id == SpiffLoggingModel.current_user_id, isouter=True
|
UserModel, UserModel.id == SpiffLoggingModel.current_user_id, isouter=True
|
||||||
) # isouter since if we don't have a user, we still want the log
|
) # isouter since if we don't have a user, we still want the log
|
||||||
@ -642,6 +669,7 @@ def message_instance_list(
|
|||||||
.add_columns(
|
.add_columns(
|
||||||
MessageModel.identifier.label("message_identifier"),
|
MessageModel.identifier.label("message_identifier"),
|
||||||
ProcessInstanceModel.process_model_identifier,
|
ProcessInstanceModel.process_model_identifier,
|
||||||
|
ProcessInstanceModel.process_model_display_name,
|
||||||
)
|
)
|
||||||
.paginate(page=page, per_page=per_page, error_out=False)
|
.paginate(page=page, per_page=per_page, error_out=False)
|
||||||
)
|
)
|
||||||
@ -776,10 +804,11 @@ def process_instance_list(
|
|||||||
with_tasks_completed_by_my_group: Optional[bool] = None,
|
with_tasks_completed_by_my_group: Optional[bool] = None,
|
||||||
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,
|
||||||
) -> 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(
|
||||||
g.user, report_identifier
|
g.user, report_id, report_identifier
|
||||||
)
|
)
|
||||||
|
|
||||||
if user_filter:
|
if user_filter:
|
||||||
@ -810,7 +839,6 @@ def process_instance_list(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# process_model_identifier = un_modify_modified_process_model_id(modified_process_model_identifier)
|
|
||||||
process_instance_query = ProcessInstanceModel.query
|
process_instance_query = ProcessInstanceModel.query
|
||||||
# Always join that hot user table for good performance at serialization time.
|
# Always join that hot user table for good performance at serialization time.
|
||||||
process_instance_query = process_instance_query.options(
|
process_instance_query = process_instance_query.options(
|
||||||
@ -928,28 +956,78 @@ def process_instance_list(
|
|||||||
UserGroupAssignmentModel.user_id == g.user.id
|
UserGroupAssignmentModel.user_id == g.user.id
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
instance_metadata_aliases = {}
|
||||||
process_instances = (
|
stock_columns = ProcessInstanceReportService.get_column_names_for_model(
|
||||||
process_instance_query.group_by(ProcessInstanceModel.id)
|
ProcessInstanceModel
|
||||||
.order_by(
|
)
|
||||||
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
|
for column in process_instance_report.report_metadata["columns"]:
|
||||||
)
|
if column["accessor"] in stock_columns:
|
||||||
.paginate(page=page, per_page=per_page, error_out=False)
|
continue
|
||||||
)
|
instance_metadata_alias = aliased(ProcessInstanceMetadataModel)
|
||||||
except Exception as e:
|
instance_metadata_aliases[column["accessor"]] = instance_metadata_alias
|
||||||
print(e)
|
|
||||||
|
filter_for_column = None
|
||||||
results = list(
|
if "filter_by" in process_instance_report.report_metadata:
|
||||||
map(
|
filter_for_column = next(
|
||||||
ProcessInstanceService.serialize_flat_with_task_data,
|
(
|
||||||
process_instances.items,
|
f
|
||||||
)
|
for f in process_instance_report.report_metadata["filter_by"]
|
||||||
|
if f["field_name"] == column["accessor"]
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
isouter = True
|
||||||
|
conditions = [
|
||||||
|
ProcessInstanceModel.id == instance_metadata_alias.process_instance_id,
|
||||||
|
instance_metadata_alias.key == column["accessor"],
|
||||||
|
]
|
||||||
|
if filter_for_column:
|
||||||
|
isouter = False
|
||||||
|
conditions.append(
|
||||||
|
instance_metadata_alias.value == filter_for_column["field_value"]
|
||||||
|
)
|
||||||
|
process_instance_query = process_instance_query.join(
|
||||||
|
instance_metadata_alias, and_(*conditions), isouter=isouter
|
||||||
|
).add_columns(func.max(instance_metadata_alias.value).label(column["accessor"]))
|
||||||
|
|
||||||
|
order_by_query_array = []
|
||||||
|
order_by_array = process_instance_report.report_metadata["order_by"]
|
||||||
|
if len(order_by_array) < 1:
|
||||||
|
order_by_array = ProcessInstanceReportModel.default_order_by()
|
||||||
|
for order_by_option in order_by_array:
|
||||||
|
attribute = re.sub("^-", "", order_by_option)
|
||||||
|
if attribute in stock_columns:
|
||||||
|
if order_by_option.startswith("-"):
|
||||||
|
order_by_query_array.append(
|
||||||
|
getattr(ProcessInstanceModel, attribute).desc()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
order_by_query_array.append(
|
||||||
|
getattr(ProcessInstanceModel, attribute).asc()
|
||||||
|
)
|
||||||
|
elif attribute in instance_metadata_aliases:
|
||||||
|
if order_by_option.startswith("-"):
|
||||||
|
order_by_query_array.append(
|
||||||
|
instance_metadata_aliases[attribute].value.desc()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
order_by_query_array.append(
|
||||||
|
instance_metadata_aliases[attribute].value.asc()
|
||||||
|
)
|
||||||
|
|
||||||
|
process_instances = (
|
||||||
|
process_instance_query.group_by(ProcessInstanceModel.id)
|
||||||
|
.add_columns(ProcessInstanceModel.id)
|
||||||
|
.order_by(*order_by_query_array)
|
||||||
|
.paginate(page=page, per_page=per_page, error_out=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
results = ProcessInstanceReportService.add_metadata_columns_to_process_instance(
|
||||||
|
process_instances.items, process_instance_report.report_metadata["columns"]
|
||||||
)
|
)
|
||||||
report_metadata = process_instance_report.report_metadata
|
|
||||||
|
|
||||||
response_json = {
|
response_json = {
|
||||||
"report_identifier": process_instance_report.identifier,
|
"report": process_instance_report,
|
||||||
"report_metadata": report_metadata,
|
|
||||||
"results": results,
|
"results": results,
|
||||||
"filters": report_filter.to_dict(),
|
"filters": report_filter.to_dict(),
|
||||||
"pagination": {
|
"pagination": {
|
||||||
@ -962,6 +1040,22 @@ def process_instance_list(
|
|||||||
return make_response(jsonify(response_json), 200)
|
return make_response(jsonify(response_json), 200)
|
||||||
|
|
||||||
|
|
||||||
|
def process_instance_report_column_list() -> flask.wrappers.Response:
|
||||||
|
"""Process_instance_report_column_list."""
|
||||||
|
table_columns = ProcessInstanceReportService.builtin_column_options()
|
||||||
|
columns_for_metadata = (
|
||||||
|
db.session.query(ProcessInstanceMetadataModel.key)
|
||||||
|
.order_by(ProcessInstanceMetadataModel.key)
|
||||||
|
.distinct() # type: ignore
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
columns_for_metadata_strings = [
|
||||||
|
{"Header": i[0], "accessor": i[0], "filterable": True}
|
||||||
|
for i in columns_for_metadata
|
||||||
|
]
|
||||||
|
return make_response(jsonify(table_columns + columns_for_metadata_strings), 200)
|
||||||
|
|
||||||
|
|
||||||
def process_instance_show(
|
def process_instance_show(
|
||||||
modified_process_model_identifier: str, process_instance_id: int
|
modified_process_model_identifier: str, process_instance_id: int
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
@ -988,7 +1082,9 @@ def process_instance_show(
|
|||||||
return make_response(jsonify(process_instance), 200)
|
return make_response(jsonify(process_instance), 200)
|
||||||
|
|
||||||
|
|
||||||
def process_instance_delete(process_instance_id: int) -> flask.wrappers.Response:
|
def process_instance_delete(
|
||||||
|
process_instance_id: int, modified_process_model_identifier: str
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
"""Create_process_instance."""
|
"""Create_process_instance."""
|
||||||
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
||||||
|
|
||||||
@ -1018,22 +1114,22 @@ def process_instance_report_list(
|
|||||||
|
|
||||||
def process_instance_report_create(body: Dict[str, Any]) -> flask.wrappers.Response:
|
def process_instance_report_create(body: Dict[str, Any]) -> flask.wrappers.Response:
|
||||||
"""Process_instance_report_create."""
|
"""Process_instance_report_create."""
|
||||||
ProcessInstanceReportModel.create_report(
|
process_instance_report = ProcessInstanceReportModel.create_report(
|
||||||
identifier=body["identifier"],
|
identifier=body["identifier"],
|
||||||
user=g.user,
|
user=g.user,
|
||||||
report_metadata=body["report_metadata"],
|
report_metadata=body["report_metadata"],
|
||||||
)
|
)
|
||||||
|
|
||||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
return make_response(jsonify(process_instance_report), 201)
|
||||||
|
|
||||||
|
|
||||||
def process_instance_report_update(
|
def process_instance_report_update(
|
||||||
report_identifier: str,
|
report_id: int,
|
||||||
body: Dict[str, Any],
|
body: Dict[str, Any],
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_report_create."""
|
"""Process_instance_report_create."""
|
||||||
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||||
identifier=report_identifier,
|
id=report_id,
|
||||||
created_by_id=g.user.id,
|
created_by_id=g.user.id,
|
||||||
).first()
|
).first()
|
||||||
if process_instance_report is None:
|
if process_instance_report is None:
|
||||||
@ -1046,15 +1142,15 @@ def process_instance_report_update(
|
|||||||
process_instance_report.report_metadata = body["report_metadata"]
|
process_instance_report.report_metadata = body["report_metadata"]
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
return make_response(jsonify(process_instance_report), 201)
|
||||||
|
|
||||||
|
|
||||||
def process_instance_report_delete(
|
def process_instance_report_delete(
|
||||||
report_identifier: str,
|
report_id: int,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_report_create."""
|
"""Process_instance_report_create."""
|
||||||
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||||
identifier=report_identifier,
|
id=report_id,
|
||||||
created_by_id=g.user.id,
|
created_by_id=g.user.id,
|
||||||
).first()
|
).first()
|
||||||
if process_instance_report is None:
|
if process_instance_report is None:
|
||||||
@ -1070,11 +1166,9 @@ def process_instance_report_delete(
|
|||||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
||||||
|
|
||||||
|
|
||||||
def service_tasks_show() -> flask.wrappers.Response:
|
def service_task_list() -> flask.wrappers.Response:
|
||||||
"""Service_tasks_show."""
|
"""Service_task_list."""
|
||||||
available_connectors = ServiceTaskService.available_connectors()
|
available_connectors = ServiceTaskService.available_connectors()
|
||||||
print(available_connectors)
|
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
json.dumps(available_connectors), status=200, mimetype="application/json"
|
json.dumps(available_connectors), status=200, mimetype="application/json"
|
||||||
)
|
)
|
||||||
@ -1108,19 +1202,17 @@ def authentication_callback(
|
|||||||
|
|
||||||
|
|
||||||
def process_instance_report_show(
|
def process_instance_report_show(
|
||||||
report_identifier: str,
|
report_id: int,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
per_page: int = 100,
|
per_page: int = 100,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_list."""
|
"""Process_instance_report_show."""
|
||||||
process_instances = ProcessInstanceModel.query.order_by( # .filter_by(process_model_identifier=process_model.id)
|
process_instances = ProcessInstanceModel.query.order_by(
|
||||||
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
|
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
|
||||||
).paginate(
|
).paginate(page=page, per_page=per_page, error_out=False)
|
||||||
page=page, per_page=per_page, error_out=False
|
|
||||||
)
|
|
||||||
|
|
||||||
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||||
identifier=report_identifier,
|
id=report_id,
|
||||||
created_by_id=g.user.id,
|
created_by_id=g.user.id,
|
||||||
).first()
|
).first()
|
||||||
if process_instance_report is None:
|
if process_instance_report is None:
|
||||||
@ -1282,7 +1374,7 @@ def get_tasks(
|
|||||||
|
|
||||||
|
|
||||||
def process_instance_task_list(
|
def process_instance_task_list(
|
||||||
modified_process_model_id: 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,
|
||||||
@ -1397,9 +1489,6 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
|
|||||||
task.form_ui_schema = ui_form_contents
|
task.form_ui_schema = ui_form_contents
|
||||||
|
|
||||||
if task.properties and task.data and "instructionsForEndUser" in task.properties:
|
if task.properties and task.data and "instructionsForEndUser" in task.properties:
|
||||||
print(
|
|
||||||
f"task.properties['instructionsForEndUser']: {task.properties['instructionsForEndUser']}"
|
|
||||||
)
|
|
||||||
if task.properties["instructionsForEndUser"]:
|
if task.properties["instructionsForEndUser"]:
|
||||||
task.properties["instructionsForEndUser"] = render_jinja_template(
|
task.properties["instructionsForEndUser"] = render_jinja_template(
|
||||||
task.properties["instructionsForEndUser"], task.data
|
task.properties["instructionsForEndUser"], task.data
|
||||||
@ -1846,7 +1935,12 @@ def _update_form_schema_with_task_data_as_needed(
|
|||||||
_update_form_schema_with_task_data_as_needed(o, task_data)
|
_update_form_schema_with_task_data_as_needed(o, task_data)
|
||||||
|
|
||||||
|
|
||||||
def update_task_data(process_instance_id: str, task_id: str, body: Dict) -> Response:
|
def update_task_data(
|
||||||
|
process_instance_id: str,
|
||||||
|
modified_process_model_identifier: str,
|
||||||
|
task_id: str,
|
||||||
|
body: Dict,
|
||||||
|
) -> Response:
|
||||||
"""Update task data."""
|
"""Update task data."""
|
||||||
process_instance = ProcessInstanceModel.query.filter(
|
process_instance = ProcessInstanceModel.query.filter(
|
||||||
ProcessInstanceModel.id == int(process_instance_id)
|
ProcessInstanceModel.id == int(process_instance_id)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
"""Get_env."""
|
"""Save process instance metadata."""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from flask_bpmn.models.db import db
|
from flask_bpmn.models.db import db
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
@ -186,8 +186,9 @@ class AuthenticationService:
|
|||||||
refresh_token_object: RefreshTokenModel = RefreshTokenModel.query.filter(
|
refresh_token_object: RefreshTokenModel = RefreshTokenModel.query.filter(
|
||||||
RefreshTokenModel.user_id == user_id
|
RefreshTokenModel.user_id == user_id
|
||||||
).first()
|
).first()
|
||||||
assert refresh_token_object # noqa: S101
|
if refresh_token_object:
|
||||||
return refresh_token_object.token
|
return refresh_token_object.token
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_auth_token_from_refresh_token(cls, refresh_token: str) -> dict:
|
def get_auth_token_from_refresh_token(cls, refresh_token: str) -> dict:
|
||||||
|
@ -81,6 +81,9 @@ from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
|||||||
from spiffworkflow_backend.models.message_instance import MessageModel
|
from spiffworkflow_backend.models.message_instance import MessageModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
||||||
|
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||||
|
ProcessInstanceMetadataModel,
|
||||||
|
)
|
||||||
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
||||||
from spiffworkflow_backend.models.script_attributes_context import (
|
from spiffworkflow_backend.models.script_attributes_context import (
|
||||||
ScriptAttributesContext,
|
ScriptAttributesContext,
|
||||||
@ -179,7 +182,12 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
|
|||||||
)
|
)
|
||||||
return Script.generate_augmented_list(script_attributes_context)
|
return Script.generate_augmented_list(script_attributes_context)
|
||||||
|
|
||||||
def evaluate(self, task: SpiffTask, expression: str, external_methods: Any = None) -> Any:
|
def evaluate(
|
||||||
|
self,
|
||||||
|
task: SpiffTask,
|
||||||
|
expression: str,
|
||||||
|
external_methods: Optional[dict[str, Any]] = None,
|
||||||
|
) -> Any:
|
||||||
"""Evaluate."""
|
"""Evaluate."""
|
||||||
return self._evaluate(expression, task.data, task, external_methods)
|
return self._evaluate(expression, task.data, task, external_methods)
|
||||||
|
|
||||||
@ -572,6 +580,36 @@ class ProcessInstanceProcessor:
|
|||||||
db.session.add(details_model)
|
db.session.add(details_model)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
def extract_metadata(self, process_model_info: ProcessModelInfo) -> None:
|
||||||
|
"""Extract_metadata."""
|
||||||
|
metadata_extraction_paths = process_model_info.metadata_extraction_paths
|
||||||
|
if metadata_extraction_paths is None:
|
||||||
|
return
|
||||||
|
if len(metadata_extraction_paths) <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
current_data = self.get_current_data()
|
||||||
|
for metadata_extraction_path in metadata_extraction_paths:
|
||||||
|
key = metadata_extraction_path["key"]
|
||||||
|
path = metadata_extraction_path["path"]
|
||||||
|
path_segments = path.split(".")
|
||||||
|
data_for_key = current_data
|
||||||
|
for path_segment in path_segments:
|
||||||
|
data_for_key = data_for_key[path_segment]
|
||||||
|
|
||||||
|
pim = ProcessInstanceMetadataModel.query.filter_by(
|
||||||
|
process_instance_id=self.process_instance_model.id,
|
||||||
|
key=key,
|
||||||
|
).first()
|
||||||
|
if pim is None:
|
||||||
|
pim = ProcessInstanceMetadataModel(
|
||||||
|
process_instance_id=self.process_instance_model.id,
|
||||||
|
key=key,
|
||||||
|
)
|
||||||
|
pim.value = data_for_key
|
||||||
|
db.session.add(pim)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
"""Saves the current state of this processor to the database."""
|
"""Saves the current state of this processor to the database."""
|
||||||
self.process_instance_model.bpmn_json = self.serialize()
|
self.process_instance_model.bpmn_json = self.serialize()
|
||||||
@ -598,6 +636,15 @@ class ProcessInstanceProcessor:
|
|||||||
process_instance_id=self.process_instance_model.id
|
process_instance_id=self.process_instance_model.id
|
||||||
).all()
|
).all()
|
||||||
ready_or_waiting_tasks = self.get_all_ready_or_waiting_tasks()
|
ready_or_waiting_tasks = self.get_all_ready_or_waiting_tasks()
|
||||||
|
process_model_display_name = ""
|
||||||
|
process_model_info = self.process_model_service.get_process_model(
|
||||||
|
self.process_instance_model.process_model_identifier
|
||||||
|
)
|
||||||
|
if process_model_info is not None:
|
||||||
|
process_model_display_name = process_model_info.display_name
|
||||||
|
|
||||||
|
self.extract_metadata(process_model_info)
|
||||||
|
|
||||||
for ready_or_waiting_task in ready_or_waiting_tasks:
|
for ready_or_waiting_task in ready_or_waiting_tasks:
|
||||||
# filter out non-usertasks
|
# filter out non-usertasks
|
||||||
task_spec = ready_or_waiting_task.task_spec
|
task_spec = ready_or_waiting_task.task_spec
|
||||||
@ -616,13 +663,6 @@ class ProcessInstanceProcessor:
|
|||||||
if "formUiSchemaFilename" in properties:
|
if "formUiSchemaFilename" in properties:
|
||||||
ui_form_file_name = properties["formUiSchemaFilename"]
|
ui_form_file_name = properties["formUiSchemaFilename"]
|
||||||
|
|
||||||
process_model_display_name = ""
|
|
||||||
process_model_info = self.process_model_service.get_process_model(
|
|
||||||
self.process_instance_model.process_model_identifier
|
|
||||||
)
|
|
||||||
if process_model_info is not None:
|
|
||||||
process_model_display_name = process_model_info.display_name
|
|
||||||
|
|
||||||
active_task = None
|
active_task = None
|
||||||
for at in active_tasks:
|
for at in active_tasks:
|
||||||
if at.task_id == str(ready_or_waiting_task.id):
|
if at.task_id == str(ready_or_waiting_task.id):
|
||||||
@ -1147,8 +1187,8 @@ class ProcessInstanceProcessor:
|
|||||||
def get_current_data(self) -> dict[str, Any]:
|
def get_current_data(self) -> dict[str, Any]:
|
||||||
"""Get the current data for the process.
|
"""Get the current data for the process.
|
||||||
|
|
||||||
Return either most recent task data or the process data
|
Return either the most recent task data or--if the process instance is complete--
|
||||||
if the process instance is complete
|
the process data.
|
||||||
"""
|
"""
|
||||||
if self.process_instance_model.status == "complete":
|
if self.process_instance_model.status == "complete":
|
||||||
return self.get_data()
|
return self.get_data()
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
from flask_bpmn.models.db import db
|
||||||
|
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
from spiffworkflow_backend.models.process_instance_report import (
|
||||||
ProcessInstanceReportModel,
|
ProcessInstanceReportModel,
|
||||||
)
|
)
|
||||||
@ -57,12 +60,21 @@ class ProcessInstanceReportService:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def report_with_identifier(
|
def report_with_identifier(
|
||||||
cls, user: UserModel, report_identifier: Optional[str] = None
|
cls,
|
||||||
|
user: UserModel,
|
||||||
|
report_id: Optional[int] = None,
|
||||||
|
report_identifier: Optional[str] = None,
|
||||||
) -> ProcessInstanceReportModel:
|
) -> ProcessInstanceReportModel:
|
||||||
"""Report_with_filter."""
|
"""Report_with_filter."""
|
||||||
|
if report_id is not None:
|
||||||
|
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||||
|
id=report_id, created_by_id=user.id
|
||||||
|
).first()
|
||||||
|
if process_instance_report is not None:
|
||||||
|
return process_instance_report # type: ignore
|
||||||
|
|
||||||
if report_identifier is None:
|
if report_identifier is None:
|
||||||
report_identifier = "default"
|
report_identifier = "default"
|
||||||
|
|
||||||
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
process_instance_report = ProcessInstanceReportModel.query.filter_by(
|
||||||
identifier=report_identifier, created_by_id=user.id
|
identifier=report_identifier, created_by_id=user.id
|
||||||
).first()
|
).first()
|
||||||
@ -73,17 +85,9 @@ class ProcessInstanceReportService:
|
|||||||
# TODO replace with system reports that are loaded on launch (or similar)
|
# TODO replace with system reports that are loaded on launch (or similar)
|
||||||
temp_system_metadata_map = {
|
temp_system_metadata_map = {
|
||||||
"default": {
|
"default": {
|
||||||
"columns": [
|
"columns": cls.builtin_column_options(),
|
||||||
{"Header": "id", "accessor": "id"},
|
"filter_by": [],
|
||||||
{
|
"order_by": ["-start_in_seconds", "-id"],
|
||||||
"Header": "process_model_display_name",
|
|
||||||
"accessor": "process_model_display_name",
|
|
||||||
},
|
|
||||||
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
|
|
||||||
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
|
|
||||||
{"Header": "username", "accessor": "username"},
|
|
||||||
{"Header": "status", "accessor": "status"},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
"system_report_instances_initiated_by_me": {
|
"system_report_instances_initiated_by_me": {
|
||||||
"columns": [
|
"columns": [
|
||||||
@ -97,48 +101,31 @@ class ProcessInstanceReportService:
|
|||||||
{"Header": "status", "accessor": "status"},
|
{"Header": "status", "accessor": "status"},
|
||||||
],
|
],
|
||||||
"filter_by": [{"field_name": "initiated_by_me", "field_value": True}],
|
"filter_by": [{"field_name": "initiated_by_me", "field_value": True}],
|
||||||
|
"order_by": ["-start_in_seconds", "-id"],
|
||||||
},
|
},
|
||||||
"system_report_instances_with_tasks_completed_by_me": {
|
"system_report_instances_with_tasks_completed_by_me": {
|
||||||
"columns": [
|
"columns": cls.builtin_column_options(),
|
||||||
{"Header": "id", "accessor": "id"},
|
|
||||||
{
|
|
||||||
"Header": "process_model_display_name",
|
|
||||||
"accessor": "process_model_display_name",
|
|
||||||
},
|
|
||||||
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
|
|
||||||
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
|
|
||||||
{"Header": "username", "accessor": "username"},
|
|
||||||
{"Header": "status", "accessor": "status"},
|
|
||||||
],
|
|
||||||
"filter_by": [
|
"filter_by": [
|
||||||
{"field_name": "with_tasks_completed_by_me", "field_value": True}
|
{"field_name": "with_tasks_completed_by_me", "field_value": True}
|
||||||
],
|
],
|
||||||
|
"order_by": ["-start_in_seconds", "-id"],
|
||||||
},
|
},
|
||||||
"system_report_instances_with_tasks_completed_by_my_groups": {
|
"system_report_instances_with_tasks_completed_by_my_groups": {
|
||||||
"columns": [
|
"columns": cls.builtin_column_options(),
|
||||||
{"Header": "id", "accessor": "id"},
|
|
||||||
{
|
|
||||||
"Header": "process_model_display_name",
|
|
||||||
"accessor": "process_model_display_name",
|
|
||||||
},
|
|
||||||
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
|
|
||||||
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
|
|
||||||
{"Header": "username", "accessor": "username"},
|
|
||||||
{"Header": "status", "accessor": "status"},
|
|
||||||
],
|
|
||||||
"filter_by": [
|
"filter_by": [
|
||||||
{
|
{
|
||||||
"field_name": "with_tasks_completed_by_my_group",
|
"field_name": "with_tasks_completed_by_my_group",
|
||||||
"field_value": True,
|
"field_value": True,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"order_by": ["-start_in_seconds", "-id"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
process_instance_report = ProcessInstanceReportModel(
|
process_instance_report = ProcessInstanceReportModel(
|
||||||
identifier=report_identifier,
|
identifier=report_identifier,
|
||||||
created_by_id=user.id,
|
created_by_id=user.id,
|
||||||
report_metadata=temp_system_metadata_map[report_identifier], # type: ignore
|
report_metadata=temp_system_metadata_map[report_identifier],
|
||||||
)
|
)
|
||||||
|
|
||||||
return process_instance_report # type: ignore
|
return process_instance_report # type: ignore
|
||||||
@ -241,3 +228,43 @@ class ProcessInstanceReportService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return report_filter
|
return report_filter
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_metadata_columns_to_process_instance(
|
||||||
|
cls,
|
||||||
|
process_instance_sqlalchemy_rows: list[sqlalchemy.engine.row.Row], # type: ignore
|
||||||
|
metadata_columns: list[dict],
|
||||||
|
) -> list[dict]:
|
||||||
|
"""Add_metadata_columns_to_process_instance."""
|
||||||
|
results = []
|
||||||
|
for process_instance in process_instance_sqlalchemy_rows:
|
||||||
|
process_instance_dict = process_instance["ProcessInstanceModel"].serialized
|
||||||
|
for metadata_column in metadata_columns:
|
||||||
|
if metadata_column["accessor"] not in process_instance_dict:
|
||||||
|
process_instance_dict[
|
||||||
|
metadata_column["accessor"]
|
||||||
|
] = process_instance[metadata_column["accessor"]]
|
||||||
|
|
||||||
|
results.append(process_instance_dict)
|
||||||
|
return results
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_column_names_for_model(cls, model: db.Model) -> list[str]: # type: ignore
|
||||||
|
"""Get_column_names_for_model."""
|
||||||
|
return [i.name for i in model.__table__.columns]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def builtin_column_options(cls) -> list[dict]:
|
||||||
|
"""Builtin_column_options."""
|
||||||
|
return [
|
||||||
|
{"Header": "Id", "accessor": "id", "filterable": False},
|
||||||
|
{
|
||||||
|
"Header": "Process",
|
||||||
|
"accessor": "process_model_display_name",
|
||||||
|
"filterable": False,
|
||||||
|
},
|
||||||
|
{"Header": "Start", "accessor": "start_in_seconds", "filterable": False},
|
||||||
|
{"Header": "End", "accessor": "end_in_seconds", "filterable": False},
|
||||||
|
{"Header": "Username", "accessor": "username", "filterable": False},
|
||||||
|
{"Header": "Status", "accessor": "status", "filterable": False},
|
||||||
|
]
|
||||||
|
@ -322,18 +322,3 @@ class ProcessInstanceService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return task
|
return task
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def serialize_flat_with_task_data(
|
|
||||||
process_instance: ProcessInstanceModel,
|
|
||||||
) -> dict[str, Any]:
|
|
||||||
"""NOTE: This is crazy slow. Put the latest task data in the database."""
|
|
||||||
"""Serialize_flat_with_task_data."""
|
|
||||||
# results = {}
|
|
||||||
# try:
|
|
||||||
# processor = ProcessInstanceProcessor(process_instance)
|
|
||||||
# process_instance.data = processor.get_current_data()
|
|
||||||
# results = process_instance.serialized_flat
|
|
||||||
# except ApiError:
|
|
||||||
results = process_instance.serialized
|
|
||||||
return results
|
|
||||||
|
@ -19,7 +19,11 @@
|
|||||||
<bpmn:scriptTask id="hot_script_task_OH_YEEEEEEEEEEEEEEEEEEEEAH" name="OHHHHHHHHHHYEEEESSSSSSSSSS">
|
<bpmn:scriptTask id="hot_script_task_OH_YEEEEEEEEEEEEEEEEEEEEAH" name="OHHHHHHHHHHYEEEESSSSSSSSSS">
|
||||||
<bpmn:incoming>Flow_0bazl8x</bpmn:incoming>
|
<bpmn:incoming>Flow_0bazl8x</bpmn:incoming>
|
||||||
<bpmn:outgoing>Flow_1mcaszp</bpmn:outgoing>
|
<bpmn:outgoing>Flow_1mcaszp</bpmn:outgoing>
|
||||||
<bpmn:script>a = 1</bpmn:script>
|
<bpmn:script>a = 1
|
||||||
|
b = 2
|
||||||
|
outer = {}
|
||||||
|
outer["inner"] = 'sweet1'
|
||||||
|
</bpmn:script>
|
||||||
</bpmn:scriptTask>
|
</bpmn:scriptTask>
|
||||||
<bpmn:endEvent id="Event_1vch1y0">
|
<bpmn:endEvent id="Event_1vch1y0">
|
||||||
<bpmn:incoming>Flow_1mcaszp</bpmn:incoming>
|
<bpmn:incoming>Flow_1mcaszp</bpmn:incoming>
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
|
||||||
|
<bpmn:process id="Process_hk6nsfl" isExecutable="true">
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>Flow_1ohrjz9</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_1ohrjz9" sourceRef="StartEvent_1" targetRef="Activity_0fah9rm" />
|
||||||
|
<bpmn:endEvent id="Event_1tk4dsv">
|
||||||
|
<bpmn:incoming>Flow_1flxgry</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_18gs4jt" sourceRef="Activity_0fah9rm" targetRef="Activity_1bvyv67" />
|
||||||
|
<bpmn:scriptTask id="Activity_0fah9rm" name="First setting of data">
|
||||||
|
<bpmn:incoming>Flow_1ohrjz9</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>Flow_18gs4jt</bpmn:outgoing>
|
||||||
|
<bpmn:script>outer = {}
|
||||||
|
invoice_number = 123
|
||||||
|
outer["inner"] = 'sweet1'
|
||||||
|
outer['time'] = time.time_ns()</bpmn:script>
|
||||||
|
</bpmn:scriptTask>
|
||||||
|
<bpmn:sequenceFlow id="Flow_1flxgry" sourceRef="Activity_1bvyv67" targetRef="Event_1tk4dsv" />
|
||||||
|
<bpmn:scriptTask id="Activity_1bvyv67" name="First setting of data">
|
||||||
|
<bpmn:incoming>Flow_18gs4jt</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>Flow_1flxgry</bpmn:outgoing>
|
||||||
|
<bpmn:script>outer["inner"] = 'sweet2'</bpmn:script>
|
||||||
|
</bpmn:scriptTask>
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_hk6nsfl">
|
||||||
|
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||||
|
<dc:Bounds x="179" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Activity_1c5bi8c_di" bpmnElement="Activity_0fah9rm">
|
||||||
|
<dc:Bounds x="270" y="137" width="100" height="80" />
|
||||||
|
<bpmndi:BPMNLabel />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Event_1tk4dsv_di" bpmnElement="Event_1tk4dsv">
|
||||||
|
<dc:Bounds x="612" y="159" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Activity_1ay4o3w_di" bpmnElement="Activity_1bvyv67">
|
||||||
|
<dc:Bounds x="430" y="137" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_1ohrjz9_di" bpmnElement="Flow_1ohrjz9">
|
||||||
|
<di:waypoint x="215" y="177" />
|
||||||
|
<di:waypoint x="270" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_18gs4jt_di" bpmnElement="Flow_18gs4jt">
|
||||||
|
<di:waypoint x="370" y="177" />
|
||||||
|
<di:waypoint x="430" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_1flxgry_di" bpmnElement="Flow_1flxgry">
|
||||||
|
<di:waypoint x="530" y="177" />
|
||||||
|
<di:waypoint x="612" y="177" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
@ -265,7 +265,7 @@ class BaseTest:
|
|||||||
)
|
)
|
||||||
modified_process_model_id = test_process_model_id.replace("/", ":")
|
modified_process_model_id = test_process_model_id.replace("/", ":")
|
||||||
response = client.post(
|
response = client.post(
|
||||||
f"/v1.0/process-models/{modified_process_model_id}/process-instances",
|
f"/v1.0/process-instances/{modified_process_model_id}",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
|
@ -57,7 +57,7 @@ class TestLoggingService(BaseTest):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
log_response = client.get(
|
log_response = client.get(
|
||||||
f"/v1.0/process-instances/{process_instance_id}/logs",
|
f"/v1.0/logs/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
assert log_response.status_code == 200
|
assert log_response.status_code == 200
|
||||||
|
@ -20,6 +20,9 @@ from spiffworkflow_backend.models.group import GroupModel
|
|||||||
from spiffworkflow_backend.models.process_group import ProcessGroup
|
from spiffworkflow_backend.models.process_group import ProcessGroup
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
||||||
|
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||||
|
ProcessInstanceMetadataModel,
|
||||||
|
)
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
from spiffworkflow_backend.models.process_instance_report import (
|
||||||
ProcessInstanceReportModel,
|
ProcessInstanceReportModel,
|
||||||
)
|
)
|
||||||
@ -330,6 +333,9 @@ class TestProcessApi(BaseTest):
|
|||||||
process_model.display_name = "Updated Display Name"
|
process_model.display_name = "Updated Display Name"
|
||||||
process_model.primary_file_name = "superduper.bpmn"
|
process_model.primary_file_name = "superduper.bpmn"
|
||||||
process_model.primary_process_id = "superduper"
|
process_model.primary_process_id = "superduper"
|
||||||
|
process_model.metadata_extraction_paths = [
|
||||||
|
{"key": "extraction1", "path": "path1"}
|
||||||
|
]
|
||||||
|
|
||||||
modified_process_model_identifier = process_model_identifier.replace("/", ":")
|
modified_process_model_identifier = process_model_identifier.replace("/", ":")
|
||||||
response = client.put(
|
response = client.put(
|
||||||
@ -343,6 +349,9 @@ class TestProcessApi(BaseTest):
|
|||||||
assert response.json["display_name"] == "Updated Display Name"
|
assert response.json["display_name"] == "Updated Display Name"
|
||||||
assert response.json["primary_file_name"] == "superduper.bpmn"
|
assert response.json["primary_file_name"] == "superduper.bpmn"
|
||||||
assert response.json["primary_process_id"] == "superduper"
|
assert response.json["primary_process_id"] == "superduper"
|
||||||
|
assert response.json["metadata_extraction_paths"] == [
|
||||||
|
{"key": "extraction1", "path": "path1"}
|
||||||
|
]
|
||||||
|
|
||||||
def test_process_model_list_all(
|
def test_process_model_list_all(
|
||||||
self,
|
self,
|
||||||
@ -903,7 +912,7 @@ class TestProcessApi(BaseTest):
|
|||||||
modified_process_model_identifier = process_model_identifier.replace("/", ":")
|
modified_process_model_identifier = process_model_identifier.replace("/", ":")
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
f"/v1.0/process-models/{modified_process_model_identifier}/process-instances",
|
f"/v1.0/process-instances/{modified_process_model_identifier}",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
@ -1145,10 +1154,11 @@ class TestProcessApi(BaseTest):
|
|||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
show_response = client.get(
|
show_response = client.get(
|
||||||
f"/v1.0/process-models/{modified_process_model_identifier}/process-instances/{process_instance_id}",
|
f"/v1.0/process-instances/{modified_process_model_identifier}/{process_instance_id}",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert show_response.json is not None
|
assert show_response.json is not None
|
||||||
|
assert show_response.status_code == 200
|
||||||
file_system_root = FileSystemService.root_path()
|
file_system_root = FileSystemService.root_path()
|
||||||
file_path = (
|
file_path = (
|
||||||
f"{file_system_root}/{process_model_identifier}/{process_model_id}.bpmn"
|
f"{file_system_root}/{process_model_identifier}/{process_model_id}.bpmn"
|
||||||
@ -1311,7 +1321,7 @@ class TestProcessApi(BaseTest):
|
|||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
f"/v1.0/process-instances/{process_instance_id}/terminate",
|
f"/v1.0/process-instances/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}/terminate",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@ -1358,7 +1368,7 @@ class TestProcessApi(BaseTest):
|
|||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
|
|
||||||
delete_response = client.delete(
|
delete_response = client.delete(
|
||||||
f"/v1.0/process-instances/{process_instance_id}",
|
f"/v1.0/process-instances/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert delete_response.status_code == 200
|
assert delete_response.status_code == 200
|
||||||
@ -1723,14 +1733,14 @@ class TestProcessApi(BaseTest):
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessInstanceReportModel.create_with_attributes(
|
report = ProcessInstanceReportModel.create_with_attributes(
|
||||||
identifier="sure",
|
identifier="sure",
|
||||||
report_metadata=report_metadata,
|
report_metadata=report_metadata,
|
||||||
user=with_super_admin_user,
|
user=with_super_admin_user,
|
||||||
)
|
)
|
||||||
|
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/v1.0/process-instances/reports/sure",
|
f"/v1.0/process-instances/reports/{report.id}",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@ -1769,14 +1779,14 @@ class TestProcessApi(BaseTest):
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessInstanceReportModel.create_with_attributes(
|
report = ProcessInstanceReportModel.create_with_attributes(
|
||||||
identifier="sure",
|
identifier="sure",
|
||||||
report_metadata=report_metadata,
|
report_metadata=report_metadata,
|
||||||
user=with_super_admin_user,
|
user=with_super_admin_user,
|
||||||
)
|
)
|
||||||
|
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/v1.0/process-instances/reports/sure?grade_level=1",
|
f"/v1.0/process-instances/reports/{report.id}?grade_level=1",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@ -1791,9 +1801,9 @@ class TestProcessApi(BaseTest):
|
|||||||
with_super_admin_user: UserModel,
|
with_super_admin_user: UserModel,
|
||||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test_process_instance_report_show_with_default_list."""
|
"""Test_process_instance_report_show_with_bad_identifier."""
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/v1.0/process-instances/reports/sure?grade_level=1",
|
"/v1.0/process-instances/reports/13000000?grade_level=1",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.status_code == 404
|
assert response.status_code == 404
|
||||||
@ -2357,7 +2367,7 @@ class TestProcessApi(BaseTest):
|
|||||||
assert process_instance.status == "user_input_required"
|
assert process_instance.status == "user_input_required"
|
||||||
|
|
||||||
client.post(
|
client.post(
|
||||||
f"/v1.0/process-instances/{process_instance_id}/suspend",
|
f"/v1.0/process-instances/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}/suspend",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
process_instance = ProcessInstanceService().get_process_instance(
|
process_instance = ProcessInstanceService().get_process_instance(
|
||||||
@ -2544,3 +2554,191 @@ class TestProcessApi(BaseTest):
|
|||||||
# make sure the new subgroup does exist
|
# make sure the new subgroup does exist
|
||||||
new_process_group = ProcessModelService.get_process_group(new_sub_path)
|
new_process_group = ProcessModelService.get_process_group(new_sub_path)
|
||||||
assert new_process_group.id == new_sub_path
|
assert new_process_group.id == new_sub_path
|
||||||
|
|
||||||
|
def test_can_get_process_instance_list_with_report_metadata(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
client: FlaskClient,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_super_admin_user: UserModel,
|
||||||
|
) -> None:
|
||||||
|
"""Test_can_get_process_instance_list_with_report_metadata."""
|
||||||
|
process_model = load_test_spec(
|
||||||
|
process_model_id="save_process_instance_metadata/save_process_instance_metadata",
|
||||||
|
bpmn_file_name="save_process_instance_metadata.bpmn",
|
||||||
|
process_model_source_directory="save_process_instance_metadata",
|
||||||
|
)
|
||||||
|
process_instance = self.create_process_instance_from_process_model(
|
||||||
|
process_model=process_model, user=with_super_admin_user
|
||||||
|
)
|
||||||
|
|
||||||
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
|
processor.do_engine_steps(save=True)
|
||||||
|
process_instance_metadata = ProcessInstanceMetadataModel.query.filter_by(
|
||||||
|
process_instance_id=process_instance.id
|
||||||
|
).all()
|
||||||
|
assert len(process_instance_metadata) == 3
|
||||||
|
|
||||||
|
report_metadata = {
|
||||||
|
"columns": [
|
||||||
|
{"Header": "ID", "accessor": "id"},
|
||||||
|
{"Header": "Status", "accessor": "status"},
|
||||||
|
{"Header": "Key One", "accessor": "key1"},
|
||||||
|
{"Header": "Key Two", "accessor": "key2"},
|
||||||
|
],
|
||||||
|
"order_by": ["status"],
|
||||||
|
"filter_by": [],
|
||||||
|
}
|
||||||
|
process_instance_report = ProcessInstanceReportModel.create_with_attributes(
|
||||||
|
identifier="sure",
|
||||||
|
report_metadata=report_metadata,
|
||||||
|
user=with_super_admin_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
f"/v1.0/process-instances?report_identifier={process_instance_report.identifier}",
|
||||||
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.json is not None
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
assert len(response.json["results"]) == 1
|
||||||
|
assert response.json["results"][0]["status"] == "complete"
|
||||||
|
assert response.json["results"][0]["id"] == process_instance.id
|
||||||
|
assert response.json["results"][0]["key1"] == "value1"
|
||||||
|
assert response.json["results"][0]["key2"] == "value2"
|
||||||
|
assert response.json["pagination"]["count"] == 1
|
||||||
|
assert response.json["pagination"]["pages"] == 1
|
||||||
|
assert response.json["pagination"]["total"] == 1
|
||||||
|
|
||||||
|
def test_can_get_process_instance_report_column_list(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
client: FlaskClient,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_super_admin_user: UserModel,
|
||||||
|
) -> None:
|
||||||
|
"""Test_can_get_process_instance_list_with_report_metadata."""
|
||||||
|
process_model = load_test_spec(
|
||||||
|
process_model_id="save_process_instance_metadata/save_process_instance_metadata",
|
||||||
|
bpmn_file_name="save_process_instance_metadata.bpmn",
|
||||||
|
process_model_source_directory="save_process_instance_metadata",
|
||||||
|
)
|
||||||
|
process_instance = self.create_process_instance_from_process_model(
|
||||||
|
process_model=process_model, user=with_super_admin_user
|
||||||
|
)
|
||||||
|
|
||||||
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
|
processor.do_engine_steps(save=True)
|
||||||
|
process_instance_metadata = ProcessInstanceMetadataModel.query.filter_by(
|
||||||
|
process_instance_id=process_instance.id
|
||||||
|
).all()
|
||||||
|
assert len(process_instance_metadata) == 3
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
"/v1.0/process-instances/reports/columns",
|
||||||
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.json is not None
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json == [
|
||||||
|
{"Header": "Id", "accessor": "id", "filterable": False},
|
||||||
|
{
|
||||||
|
"Header": "Process",
|
||||||
|
"accessor": "process_model_display_name",
|
||||||
|
"filterable": False,
|
||||||
|
},
|
||||||
|
{"Header": "Start", "accessor": "start_in_seconds", "filterable": False},
|
||||||
|
{"Header": "End", "accessor": "end_in_seconds", "filterable": False},
|
||||||
|
{"Header": "Username", "accessor": "username", "filterable": False},
|
||||||
|
{"Header": "Status", "accessor": "status", "filterable": False},
|
||||||
|
{"Header": "key1", "accessor": "key1", "filterable": True},
|
||||||
|
{"Header": "key2", "accessor": "key2", "filterable": True},
|
||||||
|
{"Header": "key3", "accessor": "key3", "filterable": True},
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_process_instance_list_can_order_by_metadata(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
client: FlaskClient,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_super_admin_user: UserModel,
|
||||||
|
) -> None:
|
||||||
|
"""Test_process_instance_list_can_order_by_metadata."""
|
||||||
|
self.create_process_group(
|
||||||
|
client, with_super_admin_user, "test_group", "test_group"
|
||||||
|
)
|
||||||
|
process_model = load_test_spec(
|
||||||
|
"test_group/hello_world",
|
||||||
|
process_model_source_directory="nested-task-data-structure",
|
||||||
|
)
|
||||||
|
ProcessModelService.update_process_model(
|
||||||
|
process_model,
|
||||||
|
{
|
||||||
|
"metadata_extraction_paths": [
|
||||||
|
{"key": "time_ns", "path": "outer.time"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
process_instance_one = self.create_process_instance_from_process_model(
|
||||||
|
process_model
|
||||||
|
)
|
||||||
|
processor = ProcessInstanceProcessor(process_instance_one)
|
||||||
|
processor.do_engine_steps(save=True)
|
||||||
|
assert process_instance_one.status == "complete"
|
||||||
|
process_instance_two = self.create_process_instance_from_process_model(
|
||||||
|
process_model
|
||||||
|
)
|
||||||
|
processor = ProcessInstanceProcessor(process_instance_two)
|
||||||
|
processor.do_engine_steps(save=True)
|
||||||
|
assert process_instance_two.status == "complete"
|
||||||
|
|
||||||
|
report_metadata = {
|
||||||
|
"columns": [
|
||||||
|
{"Header": "id", "accessor": "id"},
|
||||||
|
{"Header": "Time", "accessor": "time_ns"},
|
||||||
|
],
|
||||||
|
"order_by": ["time_ns"],
|
||||||
|
}
|
||||||
|
report_one = ProcessInstanceReportModel.create_with_attributes(
|
||||||
|
identifier="report_one",
|
||||||
|
report_metadata=report_metadata,
|
||||||
|
user=with_super_admin_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
f"/v1.0/process-instances?report_id={report_one.id}",
|
||||||
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json is not None
|
||||||
|
assert len(response.json["results"]) == 2
|
||||||
|
assert response.json["results"][0]["id"] == process_instance_one.id
|
||||||
|
assert response.json["results"][1]["id"] == process_instance_two.id
|
||||||
|
|
||||||
|
report_metadata = {
|
||||||
|
"columns": [
|
||||||
|
{"Header": "id", "accessor": "id"},
|
||||||
|
{"Header": "Time", "accessor": "time_ns"},
|
||||||
|
],
|
||||||
|
"order_by": ["-time_ns"],
|
||||||
|
}
|
||||||
|
report_two = ProcessInstanceReportModel.create_with_attributes(
|
||||||
|
identifier="report_two",
|
||||||
|
report_metadata=report_metadata,
|
||||||
|
user=with_super_admin_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
f"/v1.0/process-instances?report_id={report_two.id}",
|
||||||
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json is not None
|
||||||
|
assert len(response.json["results"]) == 2
|
||||||
|
assert response.json["results"][1]["id"] == process_instance_one.id
|
||||||
|
assert response.json["results"][0]["id"] == process_instance_two.id
|
||||||
|
@ -37,7 +37,7 @@ def test_generate_report_with_filter_by_with_variable_substitution(
|
|||||||
with_db_and_bpmn_file_cleanup: None,
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test_user_can_be_given_permission_to_administer_process_group."""
|
"""Test_generate_report_with_filter_by_with_variable_substitution."""
|
||||||
process_instances = setup_process_instances_for_reports
|
process_instances = setup_process_instances_for_reports
|
||||||
report_metadata = {
|
report_metadata = {
|
||||||
"filter_by": [
|
"filter_by": [
|
||||||
@ -61,7 +61,7 @@ def test_generate_report_with_order_by_and_one_field(
|
|||||||
with_db_and_bpmn_file_cleanup: None,
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test_user_can_be_given_permission_to_administer_process_group."""
|
"""Test_generate_report_with_order_by_and_one_field."""
|
||||||
process_instances = setup_process_instances_for_reports
|
process_instances = setup_process_instances_for_reports
|
||||||
report_metadata = {"order_by": ["test_score"]}
|
report_metadata = {"order_by": ["test_score"]}
|
||||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||||
@ -75,7 +75,7 @@ def test_generate_report_with_order_by_and_two_fields(
|
|||||||
with_db_and_bpmn_file_cleanup: None,
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test_user_can_be_given_permission_to_administer_process_group."""
|
"""Test_generate_report_with_order_by_and_two_fields."""
|
||||||
process_instances = setup_process_instances_for_reports
|
process_instances = setup_process_instances_for_reports
|
||||||
report_metadata = {"order_by": ["grade_level", "test_score"]}
|
report_metadata = {"order_by": ["grade_level", "test_score"]}
|
||||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||||
@ -89,7 +89,7 @@ def test_generate_report_with_order_by_desc(
|
|||||||
with_db_and_bpmn_file_cleanup: None,
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test_user_can_be_given_permission_to_administer_process_group."""
|
"""Test_generate_report_with_order_by_desc."""
|
||||||
process_instances = setup_process_instances_for_reports
|
process_instances = setup_process_instances_for_reports
|
||||||
report_metadata = {"order_by": ["grade_level", "-test_score"]}
|
report_metadata = {"order_by": ["grade_level", "-test_score"]}
|
||||||
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
results = do_report_with_metadata_and_instances(report_metadata, process_instances)
|
||||||
@ -103,7 +103,7 @@ def test_generate_report_with_columns(
|
|||||||
with_db_and_bpmn_file_cleanup: None,
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
setup_process_instances_for_reports: list[ProcessInstanceModel],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test_user_can_be_given_permission_to_administer_process_group."""
|
"""Test_generate_report_with_columns."""
|
||||||
process_instances = setup_process_instances_for_reports
|
process_instances = setup_process_instances_for_reports
|
||||||
report_metadata = {
|
report_metadata = {
|
||||||
"columns": [
|
"columns": [
|
||||||
|
@ -6,12 +6,16 @@ from flask.testing import FlaskClient
|
|||||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||||
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
|
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
|
||||||
|
|
||||||
|
from spiffworkflow_backend.models.process_instance_metadata import (
|
||||||
|
ProcessInstanceMetadataModel,
|
||||||
|
)
|
||||||
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
||||||
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
|
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.process_instance_processor import (
|
from spiffworkflow_backend.services.process_instance_processor import (
|
||||||
ProcessInstanceProcessor,
|
ProcessInstanceProcessor,
|
||||||
)
|
)
|
||||||
|
from spiffworkflow_backend.services.process_model_service import ProcessModelService
|
||||||
|
|
||||||
|
|
||||||
class TestProcessModel(BaseTest):
|
class TestProcessModel(BaseTest):
|
||||||
@ -123,6 +127,53 @@ class TestProcessModel(BaseTest):
|
|||||||
processor.do_engine_steps(save=True)
|
processor.do_engine_steps(save=True)
|
||||||
assert process_instance.status == "complete"
|
assert process_instance.status == "complete"
|
||||||
|
|
||||||
|
def test_extract_metadata(
|
||||||
|
self,
|
||||||
|
app: Flask,
|
||||||
|
client: FlaskClient,
|
||||||
|
with_db_and_bpmn_file_cleanup: None,
|
||||||
|
with_super_admin_user: UserModel,
|
||||||
|
) -> None:
|
||||||
|
"""Test_can_run_process_model_with_call_activities."""
|
||||||
|
self.create_process_group(
|
||||||
|
client, with_super_admin_user, "test_group", "test_group"
|
||||||
|
)
|
||||||
|
process_model = load_test_spec(
|
||||||
|
"test_group/hello_world",
|
||||||
|
process_model_source_directory="nested-task-data-structure",
|
||||||
|
)
|
||||||
|
ProcessModelService.update_process_model(
|
||||||
|
process_model,
|
||||||
|
{
|
||||||
|
"metadata_extraction_paths": [
|
||||||
|
{"key": "awesome_var", "path": "outer.inner"},
|
||||||
|
{"key": "invoice_number", "path": "invoice_number"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
process_instance = self.create_process_instance_from_process_model(
|
||||||
|
process_model
|
||||||
|
)
|
||||||
|
processor = ProcessInstanceProcessor(process_instance)
|
||||||
|
processor.do_engine_steps(save=True)
|
||||||
|
assert process_instance.status == "complete"
|
||||||
|
|
||||||
|
process_instance_metadata_awesome_var = (
|
||||||
|
ProcessInstanceMetadataModel.query.filter_by(
|
||||||
|
process_instance_id=process_instance.id, key="awesome_var"
|
||||||
|
).first()
|
||||||
|
)
|
||||||
|
assert process_instance_metadata_awesome_var is not None
|
||||||
|
assert process_instance_metadata_awesome_var.value == "sweet2"
|
||||||
|
process_instance_metadata_awesome_var = (
|
||||||
|
ProcessInstanceMetadataModel.query.filter_by(
|
||||||
|
process_instance_id=process_instance.id, key="invoice_number"
|
||||||
|
).first()
|
||||||
|
)
|
||||||
|
assert process_instance_metadata_awesome_var is not None
|
||||||
|
assert process_instance_metadata_awesome_var.value == "123"
|
||||||
|
|
||||||
def create_test_process_model(self, id: str, display_name: str) -> ProcessModelInfo:
|
def create_test_process_model(self, id: str, display_name: str) -> ProcessModelInfo:
|
||||||
"""Create_test_process_model."""
|
"""Create_test_process_model."""
|
||||||
return ProcessModelInfo(
|
return ProcessModelInfo(
|
||||||
|
22
spiffworkflow-frontend/src/components/MiniComponents.tsx
Normal file
22
spiffworkflow-frontend/src/components/MiniComponents.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
||||||
|
import { MessageInstance, ProcessInstance } from '../interfaces';
|
||||||
|
|
||||||
|
export function FormatProcessModelDisplayName(
|
||||||
|
instanceObject: ProcessInstance | MessageInstance
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
process_model_identifier: processModelIdentifier,
|
||||||
|
process_model_display_name: processModelDisplayName,
|
||||||
|
} = instanceObject;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={`/admin/process-models/${modifyProcessIdentifierForPathParam(
|
||||||
|
processModelIdentifier
|
||||||
|
)}`}
|
||||||
|
title={processModelIdentifier}
|
||||||
|
>
|
||||||
|
{processModelDisplayName}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
@ -115,7 +115,6 @@ export default function ProcessGroupForm({
|
|||||||
labelText="Display Name*"
|
labelText="Display Name*"
|
||||||
value={processGroup.display_name}
|
value={processGroup.display_name}
|
||||||
onChange={(event: any) => onDisplayNameChanged(event.target.value)}
|
onChange={(event: any) => onDisplayNameChanged(event.target.value)}
|
||||||
onBlur={(event: any) => console.log('event', event)}
|
|
||||||
/>,
|
/>,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -0,0 +1,205 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
TextInput,
|
||||||
|
Stack,
|
||||||
|
Modal,
|
||||||
|
// @ts-ignore
|
||||||
|
} from '@carbon/react';
|
||||||
|
import {
|
||||||
|
ReportFilter,
|
||||||
|
ProcessInstanceReport,
|
||||||
|
ProcessModel,
|
||||||
|
ReportColumn,
|
||||||
|
ReportMetadata,
|
||||||
|
} from '../interfaces';
|
||||||
|
import HttpService from '../services/HttpService';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
onSuccess: (..._args: any[]) => any;
|
||||||
|
columnArray: ReportColumn[];
|
||||||
|
orderBy: string;
|
||||||
|
processModelSelection: ProcessModel | null;
|
||||||
|
processStatusSelection: string[];
|
||||||
|
startFromSeconds: string | null;
|
||||||
|
startToSeconds: string | null;
|
||||||
|
endFromSeconds: string | null;
|
||||||
|
endToSeconds: string | null;
|
||||||
|
buttonText?: string;
|
||||||
|
buttonClassName?: string;
|
||||||
|
processInstanceReportSelection?: ProcessInstanceReport | null;
|
||||||
|
reportMetadata: ReportMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ProcessInstanceListSaveAsReport({
|
||||||
|
onSuccess,
|
||||||
|
columnArray,
|
||||||
|
orderBy,
|
||||||
|
processModelSelection,
|
||||||
|
processInstanceReportSelection,
|
||||||
|
processStatusSelection,
|
||||||
|
startFromSeconds,
|
||||||
|
startToSeconds,
|
||||||
|
endFromSeconds,
|
||||||
|
endToSeconds,
|
||||||
|
buttonClassName,
|
||||||
|
buttonText = 'Save as Perspective',
|
||||||
|
reportMetadata,
|
||||||
|
}: OwnProps) {
|
||||||
|
const [identifier, setIdentifier] = useState<string>(
|
||||||
|
processInstanceReportSelection?.identifier || ''
|
||||||
|
);
|
||||||
|
const [showSaveForm, setShowSaveForm] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const isEditMode = () => {
|
||||||
|
return (
|
||||||
|
processInstanceReportSelection &&
|
||||||
|
processInstanceReportSelection.identifier === identifier
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const responseHandler = (result: any) => {
|
||||||
|
if (result) {
|
||||||
|
onSuccess(result, isEditMode() ? 'edit' : 'new');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveFormClose = () => {
|
||||||
|
setIdentifier(processInstanceReportSelection?.identifier || '');
|
||||||
|
setShowSaveForm(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addProcessInstanceReport = (event: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// TODO: make a field to set this
|
||||||
|
let orderByArray = ['-start_in_seconds', '-id'];
|
||||||
|
if (orderBy) {
|
||||||
|
orderByArray = orderBy.split(',').filter((n) => n);
|
||||||
|
}
|
||||||
|
const filterByArray: any = [];
|
||||||
|
|
||||||
|
if (processModelSelection) {
|
||||||
|
filterByArray.push({
|
||||||
|
field_name: 'process_model_identifier',
|
||||||
|
field_value: processModelSelection.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processStatusSelection.length > 0) {
|
||||||
|
filterByArray.push({
|
||||||
|
field_name: 'process_status',
|
||||||
|
field_value: processStatusSelection.join(','),
|
||||||
|
operator: 'in',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startFromSeconds) {
|
||||||
|
filterByArray.push({
|
||||||
|
field_name: 'start_from',
|
||||||
|
field_value: startFromSeconds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startToSeconds) {
|
||||||
|
filterByArray.push({
|
||||||
|
field_name: 'start_to',
|
||||||
|
field_value: startToSeconds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endFromSeconds) {
|
||||||
|
filterByArray.push({
|
||||||
|
field_name: 'end_from',
|
||||||
|
field_value: endFromSeconds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endToSeconds) {
|
||||||
|
filterByArray.push({
|
||||||
|
field_name: 'end_to',
|
||||||
|
field_value: endToSeconds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reportMetadata.filter_by.forEach((reportFilter: ReportFilter) => {
|
||||||
|
columnArray.forEach((reportColumn: ReportColumn) => {
|
||||||
|
if (
|
||||||
|
reportColumn.accessor === reportFilter.field_name &&
|
||||||
|
reportColumn.filterable
|
||||||
|
) {
|
||||||
|
filterByArray.push(reportFilter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let path = `/process-instances/reports`;
|
||||||
|
let httpMethod = 'POST';
|
||||||
|
if (isEditMode() && processInstanceReportSelection) {
|
||||||
|
httpMethod = 'PUT';
|
||||||
|
path = `${path}/${processInstanceReportSelection.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path,
|
||||||
|
successCallback: responseHandler,
|
||||||
|
httpMethod,
|
||||||
|
postBody: {
|
||||||
|
identifier,
|
||||||
|
report_metadata: {
|
||||||
|
columns: columnArray,
|
||||||
|
order_by: orderByArray,
|
||||||
|
filter_by: filterByArray,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
handleSaveFormClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
let textInputComponent = null;
|
||||||
|
textInputComponent = (
|
||||||
|
<TextInput
|
||||||
|
id="identifier"
|
||||||
|
name="identifier"
|
||||||
|
labelText="Identifier"
|
||||||
|
className="no-wrap"
|
||||||
|
inline
|
||||||
|
value={identifier}
|
||||||
|
onChange={(e: any) => setIdentifier(e.target.value)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
let descriptionText =
|
||||||
|
'Save the current columns and filters as a perspective so you can come back to this view in the future.';
|
||||||
|
if (processInstanceReportSelection) {
|
||||||
|
descriptionText =
|
||||||
|
'Keep the identifier the same and click Save to update the current perspective. Change the identifier if you want to save the current view with a new name.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap={5} orientation="horizontal">
|
||||||
|
<Modal
|
||||||
|
open={showSaveForm}
|
||||||
|
modalHeading="Save Perspective"
|
||||||
|
primaryButtonText="Save"
|
||||||
|
primaryButtonDisabled={!identifier}
|
||||||
|
onRequestSubmit={addProcessInstanceReport}
|
||||||
|
onRequestClose={handleSaveFormClose}
|
||||||
|
hasScrollingContent
|
||||||
|
>
|
||||||
|
<p className="data-table-description">{descriptionText}</p>
|
||||||
|
{textInputComponent}
|
||||||
|
</Modal>
|
||||||
|
<Button
|
||||||
|
kind=""
|
||||||
|
className={buttonClassName}
|
||||||
|
onClick={() => {
|
||||||
|
setIdentifier(processInstanceReportSelection?.identifier || '');
|
||||||
|
setShowSaveForm(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{buttonText}
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Filter } from '@carbon/icons-react';
|
import { Filter, Close, AddAlt } from '@carbon/icons-react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ButtonSet,
|
ButtonSet,
|
||||||
@ -21,6 +21,13 @@ import {
|
|||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
TimePicker,
|
TimePicker,
|
||||||
|
Tag,
|
||||||
|
InlineNotification,
|
||||||
|
Stack,
|
||||||
|
Modal,
|
||||||
|
ComboBox,
|
||||||
|
TextInput,
|
||||||
|
FormLabel,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} from '@carbon/react';
|
} from '@carbon/react';
|
||||||
import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
|
import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
|
||||||
@ -49,9 +56,15 @@ import {
|
|||||||
ProcessModel,
|
ProcessModel,
|
||||||
ProcessInstanceReport,
|
ProcessInstanceReport,
|
||||||
ProcessInstance,
|
ProcessInstance,
|
||||||
|
ReportColumn,
|
||||||
|
ReportColumnForEditing,
|
||||||
|
ReportMetadata,
|
||||||
|
ReportFilter,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import ProcessModelSearch from './ProcessModelSearch';
|
import ProcessModelSearch from './ProcessModelSearch';
|
||||||
import ProcessInstanceReportSearch from './ProcessInstanceReportSearch';
|
import ProcessInstanceReportSearch from './ProcessInstanceReportSearch';
|
||||||
|
import ProcessInstanceListSaveAsReport from './ProcessInstanceListSaveAsReport';
|
||||||
|
import { FormatProcessModelDisplayName } from './MiniComponents';
|
||||||
|
|
||||||
const REFRESH_INTERVAL = 5;
|
const REFRESH_INTERVAL = 5;
|
||||||
const REFRESH_TIMEOUT = 600;
|
const REFRESH_TIMEOUT = 600;
|
||||||
@ -88,7 +101,7 @@ export default function ProcessInstanceListTable({
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [processInstances, setProcessInstances] = useState([]);
|
const [processInstances, setProcessInstances] = useState([]);
|
||||||
const [reportMetadata, setReportMetadata] = useState({});
|
const [reportMetadata, setReportMetadata] = useState<ReportMetadata | null>();
|
||||||
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
const [pagination, setPagination] = useState<PaginationObject | null>(null);
|
||||||
const [processInstanceFilters, setProcessInstanceFilters] = useState({});
|
const [processInstanceFilters, setProcessInstanceFilters] = useState({});
|
||||||
|
|
||||||
@ -125,6 +138,17 @@ export default function ProcessInstanceListTable({
|
|||||||
const [processInstanceReportSelection, setProcessInstanceReportSelection] =
|
const [processInstanceReportSelection, setProcessInstanceReportSelection] =
|
||||||
useState<ProcessInstanceReport | null>(null);
|
useState<ProcessInstanceReport | null>(null);
|
||||||
|
|
||||||
|
const [availableReportColumns, setAvailableReportColumns] = useState<
|
||||||
|
ReportColumn[]
|
||||||
|
>([]);
|
||||||
|
const [processInstanceReportJustSaved, setProcessInstanceReportJustSaved] =
|
||||||
|
useState<string | null>(null);
|
||||||
|
const [showReportColumnForm, setShowReportColumnForm] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
const [reportColumnToOperateOn, setReportColumnToOperateOn] =
|
||||||
|
useState<ReportColumnForEditing | null>(null);
|
||||||
|
const [reportColumnFormMode, setReportColumnFormMode] = useState<string>('');
|
||||||
|
|
||||||
const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
|
const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
start_from: [setStartFromDate, setStartFromTime],
|
start_from: [setStartFromDate, setStartFromTime],
|
||||||
@ -155,16 +179,12 @@ export default function ProcessInstanceListTable({
|
|||||||
function setProcessInstancesFromResult(result: any) {
|
function setProcessInstancesFromResult(result: any) {
|
||||||
const processInstancesFromApi = result.results;
|
const processInstancesFromApi = result.results;
|
||||||
setProcessInstances(processInstancesFromApi);
|
setProcessInstances(processInstancesFromApi);
|
||||||
setReportMetadata(result.report_metadata);
|
|
||||||
setPagination(result.pagination);
|
setPagination(result.pagination);
|
||||||
setProcessInstanceFilters(result.filters);
|
setProcessInstanceFilters(result.filters);
|
||||||
|
|
||||||
// TODO: need to iron out this interaction some more
|
setReportMetadata(result.report.report_metadata);
|
||||||
if (result.report_identifier !== 'default') {
|
if (result.report.id) {
|
||||||
setProcessInstanceReportSelection({
|
setProcessInstanceReportSelection(result.report);
|
||||||
id: result.report_identifier,
|
|
||||||
display_name: result.report_identifier,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getProcessInstances() {
|
function getProcessInstances() {
|
||||||
@ -186,14 +206,10 @@ export default function ProcessInstanceListTable({
|
|||||||
queryParamString += `&user_filter=${userAppliedFilter}`;
|
queryParamString += `&user_filter=${userAppliedFilter}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reportIdentifierToUse: any = reportIdentifier;
|
if (searchParams.get('report_id')) {
|
||||||
|
queryParamString += `&report_id=${searchParams.get('report_id')}`;
|
||||||
if (!reportIdentifierToUse) {
|
} else if (reportIdentifier) {
|
||||||
reportIdentifierToUse = searchParams.get('report_identifier');
|
queryParamString += `&report_identifier=${reportIdentifier}`;
|
||||||
}
|
|
||||||
|
|
||||||
if (reportIdentifierToUse) {
|
|
||||||
queryParamString += `&report_identifier=${reportIdentifierToUse}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(dateParametersToAlwaysFilterBy).forEach(
|
Object.keys(dateParametersToAlwaysFilterBy).forEach(
|
||||||
@ -349,6 +365,30 @@ export default function ProcessInstanceListTable({
|
|||||||
processModelAvailableItems,
|
processModelAvailableItems,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const processInstanceReportSaveTag = () => {
|
||||||
|
if (processInstanceReportJustSaved) {
|
||||||
|
let titleOperation = 'Updated';
|
||||||
|
if (processInstanceReportJustSaved === 'new') {
|
||||||
|
titleOperation = 'Created';
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InlineNotification
|
||||||
|
title={`Perspective ${titleOperation}:`}
|
||||||
|
subtitle={`'${
|
||||||
|
processInstanceReportSelection
|
||||||
|
? processInstanceReportSelection.identifier
|
||||||
|
: ''
|
||||||
|
}'`}
|
||||||
|
kind="success"
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
// does the comparison, but also returns false if either argument
|
// does the comparison, but also returns false if either argument
|
||||||
// is not truthy and therefore not comparable.
|
// is not truthy and therefore not comparable.
|
||||||
const isTrueComparison = (param1: any, operation: any, param2: any) => {
|
const isTrueComparison = (param1: any, operation: any, param2: any) => {
|
||||||
@ -366,16 +406,8 @@ export default function ProcessInstanceListTable({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyFilter = (event: any) => {
|
// TODO: after factoring this out page hangs when invalid date ranges and applying the filter
|
||||||
event.preventDefault();
|
const calculateStartAndEndSeconds = () => {
|
||||||
const { page, perPage } = getPageInfoFromSearchParams(
|
|
||||||
searchParams,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
paginationQueryParamPrefix
|
|
||||||
);
|
|
||||||
let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`;
|
|
||||||
|
|
||||||
const startFromSeconds = convertDateAndTimeStringsToSeconds(
|
const startFromSeconds = convertDateAndTimeStringsToSeconds(
|
||||||
startFromDate,
|
startFromDate,
|
||||||
startFromTime || '00:00:00'
|
startFromTime || '00:00:00'
|
||||||
@ -392,28 +424,59 @@ export default function ProcessInstanceListTable({
|
|||||||
endToDate,
|
endToDate,
|
||||||
endToTime || '00:00:00'
|
endToTime || '00:00:00'
|
||||||
);
|
);
|
||||||
|
let valid = true;
|
||||||
if (isTrueComparison(startFromSeconds, '>', startToSeconds)) {
|
if (isTrueComparison(startFromSeconds, '>', startToSeconds)) {
|
||||||
setErrorMessage({
|
setErrorMessage({
|
||||||
message: '"Start date from" cannot be after "start date to"',
|
message: '"Start date from" cannot be after "start date to"',
|
||||||
});
|
});
|
||||||
return;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (isTrueComparison(endFromSeconds, '>', endToSeconds)) {
|
if (isTrueComparison(endFromSeconds, '>', endToSeconds)) {
|
||||||
setErrorMessage({
|
setErrorMessage({
|
||||||
message: '"End date from" cannot be after "end date to"',
|
message: '"End date from" cannot be after "end date to"',
|
||||||
});
|
});
|
||||||
return;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (isTrueComparison(startFromSeconds, '>', endFromSeconds)) {
|
if (isTrueComparison(startFromSeconds, '>', endFromSeconds)) {
|
||||||
setErrorMessage({
|
setErrorMessage({
|
||||||
message: '"Start date from" cannot be after "end date from"',
|
message: '"Start date from" cannot be after "end date from"',
|
||||||
});
|
});
|
||||||
return;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (isTrueComparison(startToSeconds, '>', endToSeconds)) {
|
if (isTrueComparison(startToSeconds, '>', endToSeconds)) {
|
||||||
setErrorMessage({
|
setErrorMessage({
|
||||||
message: '"Start date to" cannot be after "end date to"',
|
message: '"Start date to" cannot be after "end date to"',
|
||||||
});
|
});
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid,
|
||||||
|
startFromSeconds,
|
||||||
|
startToSeconds,
|
||||||
|
endFromSeconds,
|
||||||
|
endToSeconds,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyFilter = (event: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const { page, perPage } = getPageInfoFromSearchParams(
|
||||||
|
searchParams,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
paginationQueryParamPrefix
|
||||||
|
);
|
||||||
|
let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`;
|
||||||
|
const {
|
||||||
|
valid,
|
||||||
|
startFromSeconds,
|
||||||
|
startToSeconds,
|
||||||
|
endFromSeconds,
|
||||||
|
endToSeconds,
|
||||||
|
} = calculateStartAndEndSeconds();
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,10 +501,11 @@ export default function ProcessInstanceListTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (processInstanceReportSelection) {
|
if (processInstanceReportSelection) {
|
||||||
queryParamString += `&report_identifier=${processInstanceReportSelection.id}`;
|
queryParamString += `&report_id=${processInstanceReportSelection.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
|
setProcessInstanceReportJustSaved(null);
|
||||||
navigate(`/admin/process-instances?${queryParamString}`);
|
navigate(`/admin/process-instances?${queryParamString}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -529,12 +593,369 @@ export default function ProcessInstanceListTable({
|
|||||||
setEndToTime('');
|
setEndToTime('');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const processInstanceReportDidChange = (selection: any, mode?: string) => {
|
||||||
|
clearFilters();
|
||||||
|
const selectedReport = selection.selectedItem;
|
||||||
|
setProcessInstanceReportSelection(selectedReport);
|
||||||
|
|
||||||
|
let queryParamString = '';
|
||||||
|
if (selectedReport) {
|
||||||
|
queryParamString = `?report_id=${selectedReport.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrorMessage(null);
|
||||||
|
setProcessInstanceReportJustSaved(mode || null);
|
||||||
|
navigate(`/admin/process-instances${queryParamString}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reportColumns = () => {
|
||||||
|
return (reportMetadata as any).columns;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reportColumnAccessors = () => {
|
||||||
|
return reportColumns().map((reportColumn: ReportColumn) => {
|
||||||
|
return reportColumn.accessor;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO onSuccess reload/select the new report in the report search
|
||||||
|
const onSaveReportSuccess = (result: any, mode: string) => {
|
||||||
|
processInstanceReportDidChange(
|
||||||
|
{
|
||||||
|
selectedItem: result,
|
||||||
|
},
|
||||||
|
mode
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveAsReportComponent = () => {
|
||||||
|
const {
|
||||||
|
valid,
|
||||||
|
startFromSeconds,
|
||||||
|
startToSeconds,
|
||||||
|
endFromSeconds,
|
||||||
|
endToSeconds,
|
||||||
|
} = calculateStartAndEndSeconds();
|
||||||
|
|
||||||
|
if (!valid || !reportMetadata) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ProcessInstanceListSaveAsReport
|
||||||
|
onSuccess={onSaveReportSuccess}
|
||||||
|
buttonClassName="button-white-background narrow-button"
|
||||||
|
columnArray={reportColumns()}
|
||||||
|
orderBy=""
|
||||||
|
buttonText="Save"
|
||||||
|
processModelSelection={processModelSelection}
|
||||||
|
processStatusSelection={processStatusSelection}
|
||||||
|
processInstanceReportSelection={processInstanceReportSelection}
|
||||||
|
reportMetadata={reportMetadata}
|
||||||
|
startFromSeconds={startFromSeconds}
|
||||||
|
startToSeconds={startToSeconds}
|
||||||
|
endFromSeconds={endFromSeconds}
|
||||||
|
endToSeconds={endToSeconds}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeColumn = (reportColumn: ReportColumn) => {
|
||||||
|
if (reportMetadata) {
|
||||||
|
const reportMetadataCopy = { ...reportMetadata };
|
||||||
|
const newColumns = reportColumns().filter(
|
||||||
|
(rc: ReportColumn) => rc.accessor !== reportColumn.accessor
|
||||||
|
);
|
||||||
|
Object.assign(reportMetadataCopy, { columns: newColumns });
|
||||||
|
setReportMetadata(reportMetadataCopy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleColumnFormClose = () => {
|
||||||
|
setShowReportColumnForm(false);
|
||||||
|
setReportColumnFormMode('');
|
||||||
|
setReportColumnToOperateOn(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFilterByFromReportMetadata = (reportColumnAccessor: string) => {
|
||||||
|
if (reportMetadata) {
|
||||||
|
return reportMetadata.filter_by.find((reportFilter: ReportFilter) => {
|
||||||
|
return reportColumnAccessor === reportFilter.field_name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNewFiltersFromReportForEditing = (
|
||||||
|
reportColumnForEditing: ReportColumnForEditing
|
||||||
|
) => {
|
||||||
|
if (!reportMetadata) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const reportMetadataCopy = { ...reportMetadata };
|
||||||
|
let newReportFilters = reportMetadataCopy.filter_by;
|
||||||
|
if (reportColumnForEditing.filterable) {
|
||||||
|
const newReportFilter: ReportFilter = {
|
||||||
|
field_name: reportColumnForEditing.accessor,
|
||||||
|
field_value: reportColumnForEditing.filter_field_value,
|
||||||
|
operator: reportColumnForEditing.filter_operator || 'equals',
|
||||||
|
};
|
||||||
|
const existingReportFilter = getFilterByFromReportMetadata(
|
||||||
|
reportColumnForEditing.accessor
|
||||||
|
);
|
||||||
|
if (existingReportFilter) {
|
||||||
|
const existingReportFilterIndex =
|
||||||
|
reportMetadataCopy.filter_by.indexOf(existingReportFilter);
|
||||||
|
if (reportColumnForEditing.filter_field_value) {
|
||||||
|
newReportFilters[existingReportFilterIndex] = newReportFilter;
|
||||||
|
} else {
|
||||||
|
newReportFilters.splice(existingReportFilterIndex, 1);
|
||||||
|
}
|
||||||
|
} else if (reportColumnForEditing.filter_field_value) {
|
||||||
|
newReportFilters = newReportFilters.concat([newReportFilter]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newReportFilters;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdateReportColumn = () => {
|
||||||
|
if (reportColumnToOperateOn && reportMetadata) {
|
||||||
|
const reportMetadataCopy = { ...reportMetadata };
|
||||||
|
let newReportColumns = null;
|
||||||
|
if (reportColumnFormMode === 'new') {
|
||||||
|
newReportColumns = reportColumns().concat([reportColumnToOperateOn]);
|
||||||
|
} else {
|
||||||
|
newReportColumns = reportColumns().map((rc: ReportColumn) => {
|
||||||
|
if (rc.accessor === reportColumnToOperateOn.accessor) {
|
||||||
|
return reportColumnToOperateOn;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Object.assign(reportMetadataCopy, {
|
||||||
|
columns: newReportColumns,
|
||||||
|
filter_by: getNewFiltersFromReportForEditing(reportColumnToOperateOn),
|
||||||
|
});
|
||||||
|
setReportMetadata(reportMetadataCopy);
|
||||||
|
setReportColumnToOperateOn(null);
|
||||||
|
setShowReportColumnForm(false);
|
||||||
|
setShowReportColumnForm(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reportColumnToReportColumnForEditing = (reportColumn: ReportColumn) => {
|
||||||
|
const reportColumnForEditing: ReportColumnForEditing = Object.assign(
|
||||||
|
reportColumn,
|
||||||
|
{ filter_field_value: '', filter_operator: '' }
|
||||||
|
);
|
||||||
|
const reportFilter = getFilterByFromReportMetadata(
|
||||||
|
reportColumnForEditing.accessor
|
||||||
|
);
|
||||||
|
if (reportFilter) {
|
||||||
|
reportColumnForEditing.filter_field_value = reportFilter.field_value;
|
||||||
|
reportColumnForEditing.filter_operator =
|
||||||
|
reportFilter.operator || 'equals';
|
||||||
|
}
|
||||||
|
return reportColumnForEditing;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateReportColumn = (event: any) => {
|
||||||
|
const reportColumnForEditing = reportColumnToReportColumnForEditing(
|
||||||
|
event.selectedItem
|
||||||
|
);
|
||||||
|
setReportColumnToOperateOn(reportColumnForEditing);
|
||||||
|
};
|
||||||
|
|
||||||
|
// options includes item and inputValue
|
||||||
|
const shouldFilterReportColumn = (options: any) => {
|
||||||
|
const reportColumn: ReportColumn = options.item;
|
||||||
|
const { inputValue } = options;
|
||||||
|
return (
|
||||||
|
!reportColumnAccessors().includes(reportColumn.accessor) &&
|
||||||
|
(reportColumn.accessor || '')
|
||||||
|
.toLowerCase()
|
||||||
|
.includes((inputValue || '').toLowerCase())
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setReportColumnConditionValue = (event: any) => {
|
||||||
|
if (reportColumnToOperateOn) {
|
||||||
|
const reportColumnToOperateOnCopy = {
|
||||||
|
...reportColumnToOperateOn,
|
||||||
|
};
|
||||||
|
reportColumnToOperateOnCopy.filter_field_value = event.target.value;
|
||||||
|
setReportColumnToOperateOn(reportColumnToOperateOnCopy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reportColumnForm = () => {
|
||||||
|
if (reportColumnFormMode === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const formElements = [
|
||||||
|
<TextInput
|
||||||
|
id="report-column-display-name"
|
||||||
|
name="report-column-display-name"
|
||||||
|
labelText="Display Name"
|
||||||
|
disabled={!reportColumnToOperateOn}
|
||||||
|
value={reportColumnToOperateOn ? reportColumnToOperateOn.Header : ''}
|
||||||
|
onChange={(event: any) => {
|
||||||
|
if (reportColumnToOperateOn) {
|
||||||
|
const reportColumnToOperateOnCopy = {
|
||||||
|
...reportColumnToOperateOn,
|
||||||
|
};
|
||||||
|
reportColumnToOperateOnCopy.Header = event.target.value;
|
||||||
|
setReportColumnToOperateOn(reportColumnToOperateOnCopy);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
];
|
||||||
|
if (reportColumnToOperateOn && reportColumnToOperateOn.filterable) {
|
||||||
|
formElements.push(
|
||||||
|
<TextInput
|
||||||
|
id="report-column-condition-value"
|
||||||
|
name="report-column-condition-value"
|
||||||
|
labelText="Condition Value"
|
||||||
|
value={
|
||||||
|
reportColumnToOperateOn
|
||||||
|
? reportColumnToOperateOn.filter_field_value
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
onChange={setReportColumnConditionValue}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (reportColumnFormMode === 'new') {
|
||||||
|
formElements.push(
|
||||||
|
<ComboBox
|
||||||
|
onChange={updateReportColumn}
|
||||||
|
className="combo-box-in-modal"
|
||||||
|
id="report-column-selection"
|
||||||
|
data-qa="report-column-selection"
|
||||||
|
data-modal-primary-focus
|
||||||
|
items={availableReportColumns}
|
||||||
|
itemToString={(reportColumn: ReportColumn) => {
|
||||||
|
if (reportColumn) {
|
||||||
|
return reportColumn.accessor;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}}
|
||||||
|
shouldFilterItem={shouldFilterReportColumn}
|
||||||
|
placeholder="Choose a report column"
|
||||||
|
titleText="Report Column"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const modalHeading =
|
||||||
|
reportColumnFormMode === 'new'
|
||||||
|
? 'Add Column'
|
||||||
|
: `Edit ${
|
||||||
|
reportColumnToOperateOn ? reportColumnToOperateOn.accessor : ''
|
||||||
|
} column`;
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={showReportColumnForm}
|
||||||
|
modalHeading={modalHeading}
|
||||||
|
primaryButtonText="Save"
|
||||||
|
primaryButtonDisabled={!reportColumnToOperateOn}
|
||||||
|
onRequestSubmit={handleUpdateReportColumn}
|
||||||
|
onRequestClose={handleColumnFormClose}
|
||||||
|
hasScrollingContent
|
||||||
|
>
|
||||||
|
{formElements}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnSelections = () => {
|
||||||
|
if (reportColumns()) {
|
||||||
|
const tags: any = [];
|
||||||
|
|
||||||
|
(reportColumns() as any).forEach((reportColumn: ReportColumn) => {
|
||||||
|
const reportColumnForEditing =
|
||||||
|
reportColumnToReportColumnForEditing(reportColumn);
|
||||||
|
|
||||||
|
let tagType = 'cool-gray';
|
||||||
|
let tagTypeClass = '';
|
||||||
|
if (reportColumnForEditing.filterable) {
|
||||||
|
tagType = 'green';
|
||||||
|
tagTypeClass = 'tag-type-green';
|
||||||
|
}
|
||||||
|
let reportColumnLabel = reportColumnForEditing.Header;
|
||||||
|
if (reportColumnForEditing.filter_field_value) {
|
||||||
|
reportColumnLabel = `${reportColumnLabel}=${reportColumnForEditing.filter_field_value}`;
|
||||||
|
}
|
||||||
|
tags.push(
|
||||||
|
<Tag type={tagType} size="sm">
|
||||||
|
<Button
|
||||||
|
kind="ghost"
|
||||||
|
size="sm"
|
||||||
|
className={`button-tag-icon ${tagTypeClass}`}
|
||||||
|
title={`Edit ${reportColumnForEditing.accessor}`}
|
||||||
|
onClick={() => {
|
||||||
|
setReportColumnToOperateOn(reportColumnForEditing);
|
||||||
|
setShowReportColumnForm(true);
|
||||||
|
setReportColumnFormMode('edit');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{reportColumnLabel}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
data-qa="remove-report-column"
|
||||||
|
renderIcon={Close}
|
||||||
|
iconDescription="Remove Column"
|
||||||
|
className={`button-tag-icon ${tagTypeClass}`}
|
||||||
|
hasIconOnly
|
||||||
|
size="sm"
|
||||||
|
kind="ghost"
|
||||||
|
onClick={() => removeColumn(reportColumnForEditing)}
|
||||||
|
/>
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Stack orientation="horizontal">
|
||||||
|
{tags}
|
||||||
|
<Button
|
||||||
|
data-qa="add-column-button"
|
||||||
|
renderIcon={AddAlt}
|
||||||
|
iconDescription="Filter Options"
|
||||||
|
className="with-tiny-top-margin"
|
||||||
|
kind="ghost"
|
||||||
|
hasIconOnly
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
setShowReportColumnForm(true);
|
||||||
|
setReportColumnFormMode('new');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const filterOptions = () => {
|
const filterOptions = () => {
|
||||||
if (!showFilterOptions) {
|
if (!showFilterOptions) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the columns anytime we display the filter options if they are empty
|
||||||
|
if (availableReportColumns.length < 1) {
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/process-instances/reports/columns`,
|
||||||
|
successCallback: setAvailableReportColumns,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Grid fullWidth className="with-bottom-margin">
|
||||||
|
<Column md={8} lg={16} sm={4}>
|
||||||
|
<FormLabel>Columns</FormLabel>
|
||||||
|
<br />
|
||||||
|
{columnSelections()}
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
<Grid fullWidth className="with-bottom-margin">
|
<Grid fullWidth className="with-bottom-margin">
|
||||||
<Column md={8}>
|
<Column md={8}>
|
||||||
<ProcessModelSearch
|
<ProcessModelSearch
|
||||||
@ -598,11 +1019,11 @@ export default function ProcessInstanceListTable({
|
|||||||
</Column>
|
</Column>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid fullWidth className="with-bottom-margin">
|
<Grid fullWidth className="with-bottom-margin">
|
||||||
<Column md={4}>
|
<Column sm={4} md={4} lg={8}>
|
||||||
<ButtonSet>
|
<ButtonSet>
|
||||||
<Button
|
<Button
|
||||||
kind=""
|
kind=""
|
||||||
className="button-white-background"
|
className="button-white-background narrow-button"
|
||||||
onClick={clearFilters}
|
onClick={clearFilters}
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
@ -611,11 +1032,15 @@ export default function ProcessInstanceListTable({
|
|||||||
kind="secondary"
|
kind="secondary"
|
||||||
onClick={applyFilter}
|
onClick={applyFilter}
|
||||||
data-qa="filter-button"
|
data-qa="filter-button"
|
||||||
|
className="narrow-button"
|
||||||
>
|
>
|
||||||
Filter
|
Filter
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonSet>
|
</ButtonSet>
|
||||||
</Column>
|
</Column>
|
||||||
|
<Column sm={4} md={4} lg={8}>
|
||||||
|
{saveAsReportComponent()}
|
||||||
|
</Column>
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -635,7 +1060,7 @@ export default function ProcessInstanceListTable({
|
|||||||
const getHeaderLabel = (header: string) => {
|
const getHeaderLabel = (header: string) => {
|
||||||
return headerLabels[header] ?? header;
|
return headerLabels[header] ?? header;
|
||||||
};
|
};
|
||||||
const headers = (reportMetadata as any).columns.map((column: any) => {
|
const headers = reportColumns().map((column: any) => {
|
||||||
// return <th>{getHeaderLabel((column as any).Header)}</th>;
|
// return <th>{getHeaderLabel((column as any).Header)}</th>;
|
||||||
return getHeaderLabel((column as any).Header);
|
return getHeaderLabel((column as any).Header);
|
||||||
});
|
});
|
||||||
@ -646,7 +1071,7 @@ export default function ProcessInstanceListTable({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${id}`}
|
to={`/admin/process-instances/${modifiedProcessModelId}/${id}`}
|
||||||
title={`View process instance ${id}`}
|
title={`View process instance ${id}`}
|
||||||
>
|
>
|
||||||
{id}
|
{id}
|
||||||
@ -665,22 +1090,6 @@ export default function ProcessInstanceListTable({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatProcessModelDisplayName = (
|
|
||||||
row: ProcessInstance,
|
|
||||||
displayName: string
|
|
||||||
) => {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
to={`/admin/process-models/${modifyProcessIdentifierForPathParam(
|
|
||||||
row.process_model_identifier
|
|
||||||
)}`}
|
|
||||||
title={row.process_model_identifier}
|
|
||||||
>
|
|
||||||
{displayName}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatSecondsForDisplay = (_row: any, seconds: any) => {
|
const formatSecondsForDisplay = (_row: any, seconds: any) => {
|
||||||
return convertSecondsToFormattedDateTime(seconds) || '-';
|
return convertSecondsToFormattedDateTime(seconds) || '-';
|
||||||
};
|
};
|
||||||
@ -688,15 +1097,16 @@ export default function ProcessInstanceListTable({
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const columnFormatters: Record<string, any> = {
|
const reportColumnFormatters: Record<string, any> = {
|
||||||
id: formatProcessInstanceId,
|
id: formatProcessInstanceId,
|
||||||
process_model_identifier: formatProcessModelIdentifier,
|
process_model_identifier: formatProcessModelIdentifier,
|
||||||
process_model_display_name: formatProcessModelDisplayName,
|
process_model_display_name: FormatProcessModelDisplayName,
|
||||||
start_in_seconds: formatSecondsForDisplay,
|
start_in_seconds: formatSecondsForDisplay,
|
||||||
end_in_seconds: formatSecondsForDisplay,
|
end_in_seconds: formatSecondsForDisplay,
|
||||||
};
|
};
|
||||||
const formattedColumn = (row: any, column: any) => {
|
const formattedColumn = (row: any, column: any) => {
|
||||||
const formatter = columnFormatters[column.accessor] ?? defaultFormatter;
|
const formatter =
|
||||||
|
reportColumnFormatters[column.accessor] ?? defaultFormatter;
|
||||||
const value = row[column.accessor];
|
const value = row[column.accessor];
|
||||||
if (column.accessor === 'status') {
|
if (column.accessor === 'status') {
|
||||||
return (
|
return (
|
||||||
@ -709,7 +1119,7 @@ export default function ProcessInstanceListTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rows = processInstances.map((row: any) => {
|
const rows = processInstances.map((row: any) => {
|
||||||
const currentRow = (reportMetadata as any).columns.map((column: any) => {
|
const currentRow = reportColumns().map((column: any) => {
|
||||||
return formattedColumn(row, column);
|
return formattedColumn(row, column);
|
||||||
});
|
});
|
||||||
return <tr key={row.id}>{currentRow}</tr>;
|
return <tr key={row.id}>{currentRow}</tr>;
|
||||||
@ -738,27 +1148,20 @@ export default function ProcessInstanceListTable({
|
|||||||
setShowFilterOptions(!showFilterOptions);
|
setShowFilterOptions(!showFilterOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
const processInstanceReportDidChange = (selection: any) => {
|
|
||||||
clearFilters();
|
|
||||||
|
|
||||||
const selectedReport = selection.selectedItem;
|
|
||||||
setProcessInstanceReportSelection(selectedReport);
|
|
||||||
|
|
||||||
const queryParamString = selectedReport
|
|
||||||
? `&report_identifier=${selectedReport.id}`
|
|
||||||
: '';
|
|
||||||
|
|
||||||
setErrorMessage(null);
|
|
||||||
navigate(`/admin/process-instances?${queryParamString}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const reportSearchComponent = () => {
|
const reportSearchComponent = () => {
|
||||||
if (showReports) {
|
if (showReports) {
|
||||||
|
const columns = [
|
||||||
|
<Column sm={2} md={4} lg={7}>
|
||||||
|
<ProcessInstanceReportSearch
|
||||||
|
onChange={processInstanceReportDidChange}
|
||||||
|
selectedItem={processInstanceReportSelection}
|
||||||
|
/>
|
||||||
|
</Column>,
|
||||||
|
];
|
||||||
return (
|
return (
|
||||||
<ProcessInstanceReportSearch
|
<Grid className="with-tiny-bottom-margin" fullWidth>
|
||||||
onChange={processInstanceReportDidChange}
|
{columns}
|
||||||
selectedItem={processInstanceReportSelection}
|
</Grid>
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -771,6 +1174,9 @@ export default function ProcessInstanceListTable({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid fullWidth>
|
<Grid fullWidth>
|
||||||
|
<Column sm={2} md={4} lg={7}>
|
||||||
|
{reportSearchComponent()}
|
||||||
|
</Column>
|
||||||
<Column
|
<Column
|
||||||
className="filterIcon"
|
className="filterIcon"
|
||||||
sm={{ span: 1, offset: 3 }}
|
sm={{ span: 1, offset: 3 }}
|
||||||
@ -806,8 +1212,9 @@ export default function ProcessInstanceListTable({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{reportColumnForm()}
|
||||||
|
{processInstanceReportSaveTag()}
|
||||||
{filterComponent()}
|
{filterComponent()}
|
||||||
{reportSearchComponent()}
|
|
||||||
<PaginationForTable
|
<PaginationForTable
|
||||||
page={page}
|
page={page}
|
||||||
perPage={perPage}
|
perPage={perPage}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
ComboBox,
|
ComboBox,
|
||||||
|
Stack,
|
||||||
|
FormLabel,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} from '@carbon/react';
|
} from '@carbon/react';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { truncateString } from '../helpers';
|
import { truncateString } from '../helpers';
|
||||||
import { ProcessInstanceReport } from '../interfaces';
|
import { ProcessInstanceReport } from '../interfaces';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
@ -22,52 +25,61 @@ export default function ProcessInstanceReportSearch({
|
|||||||
ProcessInstanceReport[] | null
|
ProcessInstanceReport[] | null
|
||||||
>(null);
|
>(null);
|
||||||
|
|
||||||
function setProcessInstanceReportsFromResult(result: any) {
|
const [searchParams] = useSearchParams();
|
||||||
const processInstanceReportsFromApi = result.map((item: any) => {
|
const reportId = searchParams.get('report_id');
|
||||||
return { id: item.identifier, display_name: item.identifier };
|
|
||||||
});
|
useEffect(() => {
|
||||||
setProcessInstanceReports(processInstanceReportsFromApi);
|
function setProcessInstanceReportsFromResult(
|
||||||
}
|
result: ProcessInstanceReport[]
|
||||||
|
) {
|
||||||
|
setProcessInstanceReports(result);
|
||||||
|
}
|
||||||
|
|
||||||
if (processInstanceReports === null) {
|
|
||||||
setProcessInstanceReports([]);
|
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/reports`,
|
path: `/process-instances/reports`,
|
||||||
successCallback: setProcessInstanceReportsFromResult,
|
successCallback: setProcessInstanceReportsFromResult,
|
||||||
});
|
});
|
||||||
}
|
}, [reportId]);
|
||||||
|
|
||||||
|
const reportSelectionString = (
|
||||||
|
processInstanceReport: ProcessInstanceReport
|
||||||
|
) => {
|
||||||
|
return `${truncateString(processInstanceReport.identifier, 20)} (Id: ${
|
||||||
|
processInstanceReport.id
|
||||||
|
})`;
|
||||||
|
};
|
||||||
|
|
||||||
const shouldFilterProcessInstanceReport = (options: any) => {
|
const shouldFilterProcessInstanceReport = (options: any) => {
|
||||||
const processInstanceReport: ProcessInstanceReport = options.item;
|
const processInstanceReport: ProcessInstanceReport = options.item;
|
||||||
const { inputValue } = options;
|
const { inputValue } = options;
|
||||||
return `${processInstanceReport.id} (${processInstanceReport.display_name})`.includes(
|
return reportSelectionString(processInstanceReport).includes(inputValue);
|
||||||
inputValue
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const reportsAvailable = () => {
|
const reportsAvailable = () => {
|
||||||
return processInstanceReports && processInstanceReports.length > 0;
|
return processInstanceReports && processInstanceReports.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
return reportsAvailable() ? (
|
if (reportsAvailable()) {
|
||||||
<ComboBox
|
return (
|
||||||
onChange={onChange}
|
<Stack orientation="horizontal" gap={2}>
|
||||||
id="process-instance-report-select"
|
<FormLabel className="with-top-margin">{titleText}</FormLabel>
|
||||||
data-qa="process-instance-report-selection"
|
<ComboBox
|
||||||
items={processInstanceReports}
|
onChange={onChange}
|
||||||
itemToString={(processInstanceReport: ProcessInstanceReport) => {
|
id="process-instance-report-select"
|
||||||
if (processInstanceReport) {
|
data-qa="process-instance-report-selection"
|
||||||
return `${processInstanceReport.id} (${truncateString(
|
items={processInstanceReports}
|
||||||
processInstanceReport.display_name,
|
itemToString={(processInstanceReport: ProcessInstanceReport) => {
|
||||||
20
|
if (processInstanceReport) {
|
||||||
)})`;
|
return reportSelectionString(processInstanceReport);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}}
|
}}
|
||||||
shouldFilterItem={shouldFilterProcessInstanceReport}
|
shouldFilterItem={shouldFilterProcessInstanceReport}
|
||||||
placeholder="Choose a process instance perspective"
|
placeholder="Choose a process instance perspective"
|
||||||
titleText={titleText}
|
selectedItem={selectedItem}
|
||||||
selectedItem={selectedItem}
|
/>
|
||||||
/>
|
</Stack>
|
||||||
) : null;
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -83,9 +83,9 @@ export default function ProcessInstanceRun({
|
|||||||
processModel.id
|
processModel.id
|
||||||
);
|
);
|
||||||
|
|
||||||
const processInstanceActionPath = `/v1.0/process-models/${modifiedProcessModelId}/process-instances`;
|
const processInstanceCreatePath = `/v1.0/process-instances/${modifiedProcessModelId}`;
|
||||||
let permissionRequestData: PermissionsToCheck = {
|
let permissionRequestData: PermissionsToCheck = {
|
||||||
[processInstanceActionPath]: ['POST'],
|
[processInstanceCreatePath]: ['POST'],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!checkPermissions) {
|
if (!checkPermissions) {
|
||||||
@ -117,14 +117,14 @@ export default function ProcessInstanceRun({
|
|||||||
|
|
||||||
const processInstanceCreateAndRun = () => {
|
const processInstanceCreateAndRun = () => {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: processInstanceActionPath,
|
path: processInstanceCreatePath,
|
||||||
successCallback: processModelRun,
|
successCallback: processModelRun,
|
||||||
httpMethod: 'POST',
|
httpMethod: 'POST',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (checkPermissions) {
|
if (checkPermissions) {
|
||||||
return (
|
return (
|
||||||
<Can I="POST" a={processInstanceActionPath} ability={ability}>
|
<Can I="POST" a={processInstanceCreatePath} ability={ability}>
|
||||||
<Button onClick={processInstanceCreateAndRun} className={className}>
|
<Button onClick={processInstanceCreateAndRun} className={className}>
|
||||||
Start
|
Start
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
ButtonSet,
|
||||||
|
Form,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Grid,
|
||||||
|
Column,
|
||||||
|
// @ts-ignore
|
||||||
|
} from '@carbon/react';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react';
|
import { AddAlt, TrashCan } from '@carbon/icons-react';
|
||||||
import { modifyProcessIdentifierForPathParam, slugifyString } from '../helpers';
|
import { modifyProcessIdentifierForPathParam, slugifyString } from '../helpers';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
import { ProcessModel } from '../interfaces';
|
import { MetadataExtractionPath, ProcessModel } from '../interfaces';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
mode: string;
|
mode: string;
|
||||||
@ -23,6 +33,7 @@ export default function ProcessModelForm({
|
|||||||
const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] =
|
const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [displayNameInvalid, setDisplayNameInvalid] = useState<boolean>(false);
|
const [displayNameInvalid, setDisplayNameInvalid] = useState<boolean>(false);
|
||||||
|
useState<boolean>(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const navigateToProcessModel = (result: ProcessModel) => {
|
const navigateToProcessModel = (result: ProcessModel) => {
|
||||||
@ -64,6 +75,7 @@ export default function ProcessModelForm({
|
|||||||
const postBody = {
|
const postBody = {
|
||||||
display_name: processModel.display_name,
|
display_name: processModel.display_name,
|
||||||
description: processModel.description,
|
description: processModel.description,
|
||||||
|
metadata_extraction_paths: processModel.metadata_extraction_paths,
|
||||||
};
|
};
|
||||||
if (mode === 'new') {
|
if (mode === 'new') {
|
||||||
Object.assign(postBody, {
|
Object.assign(postBody, {
|
||||||
@ -87,6 +99,80 @@ export default function ProcessModelForm({
|
|||||||
setProcessModel(processModelToCopy);
|
setProcessModel(processModelToCopy);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const metadataExtractionPathForm = (
|
||||||
|
index: number,
|
||||||
|
metadataExtractionPath: MetadataExtractionPath
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
<Column md={3} lg={7} sm={1}>
|
||||||
|
<TextInput
|
||||||
|
id={`process-model-metadata-extraction-path-key-${index}`}
|
||||||
|
labelText="Extraction Key"
|
||||||
|
value={metadataExtractionPath.key}
|
||||||
|
onChange={(event: any) => {
|
||||||
|
const cep: MetadataExtractionPath[] =
|
||||||
|
processModel.metadata_extraction_paths || [];
|
||||||
|
const newMeta = { ...metadataExtractionPath };
|
||||||
|
newMeta.key = event.target.value;
|
||||||
|
cep[index] = newMeta;
|
||||||
|
updateProcessModel({ metadata_extraction_paths: cep });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
<Column md={4} lg={8} sm={2}>
|
||||||
|
<TextInput
|
||||||
|
id={`process-model-metadata-extraction-path-${index}`}
|
||||||
|
labelText="Extraction Path"
|
||||||
|
value={metadataExtractionPath.path}
|
||||||
|
onChange={(event: any) => {
|
||||||
|
const cep: MetadataExtractionPath[] =
|
||||||
|
processModel.metadata_extraction_paths || [];
|
||||||
|
const newMeta = { ...metadataExtractionPath };
|
||||||
|
newMeta.path = event.target.value;
|
||||||
|
cep[index] = newMeta;
|
||||||
|
updateProcessModel({ metadata_extraction_paths: cep });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
<Column md={1} lg={1} sm={1}>
|
||||||
|
<Button
|
||||||
|
kind="ghost"
|
||||||
|
renderIcon={TrashCan}
|
||||||
|
iconDescription="Remove Key"
|
||||||
|
hasIconOnly
|
||||||
|
size="lg"
|
||||||
|
className="with-extra-top-margin"
|
||||||
|
onClick={() => {
|
||||||
|
const cep: MetadataExtractionPath[] =
|
||||||
|
processModel.metadata_extraction_paths || [];
|
||||||
|
cep.splice(index, 1);
|
||||||
|
updateProcessModel({ metadata_extraction_paths: cep });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const metadataExtractionPathFormArea = () => {
|
||||||
|
if (processModel.metadata_extraction_paths) {
|
||||||
|
return processModel.metadata_extraction_paths.map(
|
||||||
|
(metadataExtractionPath: MetadataExtractionPath, index: number) => {
|
||||||
|
return metadataExtractionPathForm(index, metadataExtractionPath);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addBlankMetadataExtractionPath = () => {
|
||||||
|
const cep: MetadataExtractionPath[] =
|
||||||
|
processModel.metadata_extraction_paths || [];
|
||||||
|
cep.push({ key: '', path: '' });
|
||||||
|
updateProcessModel({ metadata_extraction_paths: cep });
|
||||||
|
};
|
||||||
|
|
||||||
const onDisplayNameChanged = (newDisplayName: any) => {
|
const onDisplayNameChanged = (newDisplayName: any) => {
|
||||||
setDisplayNameInvalid(false);
|
setDisplayNameInvalid(false);
|
||||||
const updateDict = { display_name: newDisplayName };
|
const updateDict = { display_name: newDisplayName };
|
||||||
@ -108,7 +194,6 @@ export default function ProcessModelForm({
|
|||||||
onChange={(event: any) => {
|
onChange={(event: any) => {
|
||||||
onDisplayNameChanged(event.target.value);
|
onDisplayNameChanged(event.target.value);
|
||||||
}}
|
}}
|
||||||
onBlur={(event: any) => console.log('event', event)}
|
|
||||||
/>,
|
/>,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -145,6 +230,38 @@ export default function ProcessModelForm({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
textInputs.push(<h2>Metadata Extractions</h2>);
|
||||||
|
textInputs.push(
|
||||||
|
<Grid>
|
||||||
|
<Column md={8} lg={16} sm={4}>
|
||||||
|
<p className="data-table-description">
|
||||||
|
You can provide one or more metadata extractions to pull data from
|
||||||
|
your process instances to provide quick access in searches and
|
||||||
|
perspectives.
|
||||||
|
</p>
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
textInputs.push(<>{metadataExtractionPathFormArea()}</>);
|
||||||
|
textInputs.push(
|
||||||
|
<Grid>
|
||||||
|
<Column md={4} lg={8} sm={2}>
|
||||||
|
<Button
|
||||||
|
data-qa="add-metadata-extraction-path-button"
|
||||||
|
renderIcon={AddAlt}
|
||||||
|
className="button-white-background"
|
||||||
|
kind=""
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
addBlankMetadataExtractionPath();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Metadata Extraction Path
|
||||||
|
</Button>
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
|
||||||
return textInputs;
|
return textInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,9 +54,9 @@ export default function ProcessModelListTiles({
|
|||||||
<p>
|
<p>
|
||||||
Process Instance {processInstance.id} kicked off (
|
Process Instance {processInstance.id} kicked off (
|
||||||
<Link
|
<Link
|
||||||
to={`/admin/process-models/${modifyProcessIdentifierForPathParam(
|
to={`/admin/process-instances/${modifyProcessIdentifierForPathParam(
|
||||||
processInstance.process_model_identifier
|
processInstance.process_model_identifier
|
||||||
)}/process-instances/${processInstance.id}`}
|
)}/${processInstance.id}`}
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
>
|
>
|
||||||
view
|
view
|
||||||
|
@ -55,7 +55,7 @@ export default function MyOpenProcesses() {
|
|||||||
<td>
|
<td>
|
||||||
<Link
|
<Link
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}/process-instances/${rowToUse.process_instance_id}`}
|
to={`/admin/process-instances/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
|
||||||
title={`View process instance ${rowToUse.process_instance_id}`}
|
title={`View process instance ${rowToUse.process_instance_id}`}
|
||||||
>
|
>
|
||||||
{rowToUse.process_instance_id}
|
{rowToUse.process_instance_id}
|
||||||
|
@ -47,7 +47,7 @@ export default function TasksWaitingForMe() {
|
|||||||
<td>
|
<td>
|
||||||
<Link
|
<Link
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}/process-instances/${rowToUse.process_instance_id}`}
|
to={`/admin/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
|
||||||
title={`View process instance ${rowToUse.process_instance_id}`}
|
title={`View process instance ${rowToUse.process_instance_id}`}
|
||||||
>
|
>
|
||||||
{rowToUse.process_instance_id}
|
{rowToUse.process_instance_id}
|
||||||
|
@ -55,7 +55,7 @@ export default function TasksWaitingForMyGroups() {
|
|||||||
<td>
|
<td>
|
||||||
<Link
|
<Link
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}/process-instances/${rowToUse.process_instance_id}`}
|
to={`/admin/process-instances/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
|
||||||
title={`View process instance ${rowToUse.process_instance_id}`}
|
title={`View process instance ${rowToUse.process_instance_id}`}
|
||||||
>
|
>
|
||||||
{rowToUse.process_instance_id}
|
{rowToUse.process_instance_id}
|
||||||
|
@ -14,6 +14,7 @@ export const PROCESS_STATUSES = [
|
|||||||
'complete',
|
'complete',
|
||||||
'error',
|
'error',
|
||||||
'suspended',
|
'suspended',
|
||||||
|
'terminated',
|
||||||
];
|
];
|
||||||
|
|
||||||
// with time: yyyy-MM-dd HH:mm:ss
|
// with time: yyyy-MM-dd HH:mm:ss
|
||||||
|
@ -9,10 +9,12 @@ export const useUriListForPermissions = () => {
|
|||||||
messageInstanceListPath: '/v1.0/messages',
|
messageInstanceListPath: '/v1.0/messages',
|
||||||
processGroupListPath: '/v1.0/process-groups',
|
processGroupListPath: '/v1.0/process-groups',
|
||||||
processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`,
|
processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`,
|
||||||
processInstanceActionPath: `/v1.0/process-models/${params.process_model_id}/process-instances`,
|
processInstanceCreatePath: `/v1.0/process-instances/${params.process_model_id}`,
|
||||||
|
processInstanceActionPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}`,
|
||||||
processInstanceListPath: '/v1.0/process-instances',
|
processInstanceListPath: '/v1.0/process-instances',
|
||||||
processInstanceTaskListPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}/tasks`,
|
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}`,
|
||||||
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}`,
|
||||||
|
@ -69,6 +69,24 @@ h2 {
|
|||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* match normal link colors */
|
||||||
|
.cds--btn--ghost.button-link {
|
||||||
|
color: #0062fe;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.cds--btn--ghost.button-link:visited {
|
||||||
|
color: #0062fe;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.cds--btn--ghost.button-link:hover {
|
||||||
|
color: #0062fe;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.cds--btn--ghost.button-link:visited:hover {
|
||||||
|
color: #0062fe;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.cds--header__global .cds--btn--primary {
|
.cds--header__global .cds--btn--primary {
|
||||||
background-color: #161616
|
background-color: #161616
|
||||||
}
|
}
|
||||||
@ -151,10 +169,22 @@ h1.with-icons {
|
|||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.with-extra-top-margin {
|
||||||
|
margin-top: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.with-tiny-top-margin {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.with-large-bottom-margin {
|
.with-large-bottom-margin {
|
||||||
margin-bottom: 3em;
|
margin-bottom: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.with-tiny-bottom-margin {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.diagram-viewer-canvas {
|
.diagram-viewer-canvas {
|
||||||
border:1px solid #000000;
|
border:1px solid #000000;
|
||||||
height:70vh;
|
height:70vh;
|
||||||
@ -297,3 +327,38 @@ td.actions-cell {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cds--btn--ghost:not([disabled]) svg.red-icon {
|
||||||
|
fill: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failure-string {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cds--btn--ghost.cds--btn--sm.button-tag-icon {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .no-wrap cds--label cds--label--inline cds--label--inline--md{ */
|
||||||
|
.no-wrap .cds--label--inline{
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combo-box-in-modal {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cds--btn.narrow-button {
|
||||||
|
max-width: 10rem;
|
||||||
|
min-width: 5rem;
|
||||||
|
word-break: normal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-type-green:hover {
|
||||||
|
background-color: #00FF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,8 @@ export interface RecentProcessModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ProcessReference {
|
export interface ProcessReference {
|
||||||
id: string; // The unique id of the process or decision table.
|
|
||||||
name: string; // The process or decision Display name.
|
name: string; // The process or decision Display name.
|
||||||
identifier: string;
|
identifier: string; // The unique id of the process
|
||||||
display_name: string;
|
display_name: string;
|
||||||
process_group_id: string;
|
process_group_id: string;
|
||||||
process_model_id: string;
|
process_model_id: string;
|
||||||
@ -39,11 +38,58 @@ export interface ProcessFile {
|
|||||||
export interface ProcessInstance {
|
export interface ProcessInstance {
|
||||||
id: number;
|
id: number;
|
||||||
process_model_identifier: string;
|
process_model_identifier: string;
|
||||||
|
process_model_display_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageCorrelationProperties {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageCorrelations {
|
||||||
|
[key: string]: MessageCorrelationProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageInstance {
|
||||||
|
id: number;
|
||||||
|
process_model_identifier: string;
|
||||||
|
process_model_display_name: string;
|
||||||
|
process_instance_id: number;
|
||||||
|
message_identifier: string;
|
||||||
|
message_type: string;
|
||||||
|
failure_cause: string;
|
||||||
|
status: string;
|
||||||
|
created_at_in_seconds: number;
|
||||||
|
message_correlations?: MessageCorrelations;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReportFilter {
|
||||||
|
field_name: string;
|
||||||
|
field_value: string;
|
||||||
|
operator?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReportColumn {
|
||||||
|
Header: string;
|
||||||
|
accessor: string;
|
||||||
|
filterable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReportColumnForEditing extends ReportColumn {
|
||||||
|
filter_field_value: string;
|
||||||
|
filter_operator: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReportMetadata {
|
||||||
|
columns: ReportColumn[];
|
||||||
|
filter_by: ReportFilter[];
|
||||||
|
order_by: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProcessInstanceReport {
|
export interface ProcessInstanceReport {
|
||||||
id: string;
|
id: number;
|
||||||
display_name: string;
|
identifier: string;
|
||||||
|
name: string;
|
||||||
|
report_metadata: ReportMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProcessGroupLite {
|
export interface ProcessGroupLite {
|
||||||
@ -51,6 +97,11 @@ export interface ProcessGroupLite {
|
|||||||
display_name: string;
|
display_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MetadataExtractionPath {
|
||||||
|
key: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProcessModel {
|
export interface ProcessModel {
|
||||||
id: string;
|
id: string;
|
||||||
description: string;
|
description: string;
|
||||||
@ -58,6 +109,7 @@ export interface ProcessModel {
|
|||||||
primary_file_name: string;
|
primary_file_name: string;
|
||||||
files: ProcessFile[];
|
files: ProcessFile[];
|
||||||
parent_groups?: ProcessGroupLite[];
|
parent_groups?: ProcessGroupLite[];
|
||||||
|
metadata_extraction_paths?: MetadataExtractionPath[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProcessGroup {
|
export interface ProcessGroup {
|
||||||
|
@ -71,11 +71,11 @@ export default function AdminRoutes() {
|
|||||||
element={<ProcessModelEdit />}
|
element={<ProcessModelEdit />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="process-models/:process_model_id/process-instances/:process_instance_id"
|
path="process-instances/:process_model_id/:process_instance_id"
|
||||||
element={<ProcessInstanceShow />}
|
element={<ProcessInstanceShow />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="process-models/:process_model_id/process-instances/:process_instance_id/:spiff_step"
|
path="process-instances/:process_model_id/:process_instance_id/:spiff_step"
|
||||||
element={<ProcessInstanceShow />}
|
element={<ProcessInstanceShow />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
@ -103,7 +103,7 @@ export default function AdminRoutes() {
|
|||||||
element={<ReactFormEditor />}
|
element={<ReactFormEditor />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="process-models/:process_model_id/process-instances/:process_instance_id/logs"
|
path="logs/:process_model_id/:process_instance_id"
|
||||||
element={<ProcessInstanceLogList />}
|
element={<ProcessInstanceLogList />}
|
||||||
/>
|
/>
|
||||||
<Route path="process-instances" element={<ProcessInstanceList />} />
|
<Route path="process-instances" element={<ProcessInstanceList />} />
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Table } from '@carbon/react';
|
import { ErrorOutline } from '@carbon/icons-react';
|
||||||
|
// @ts-ignore
|
||||||
|
import { Table, Modal, Button } from '@carbon/react';
|
||||||
import { Link, useParams, useSearchParams } from 'react-router-dom';
|
import { Link, useParams, useSearchParams } from 'react-router-dom';
|
||||||
import PaginationForTable from '../components/PaginationForTable';
|
import PaginationForTable from '../components/PaginationForTable';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import {
|
import {
|
||||||
convertSecondsToFormattedDateString,
|
convertSecondsToFormattedDateTime,
|
||||||
getPageInfoFromSearchParams,
|
getPageInfoFromSearchParams,
|
||||||
modifyProcessIdentifierForPathParam,
|
modifyProcessIdentifierForPathParam,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
|
import { FormatProcessModelDisplayName } from '../components/MiniComponents';
|
||||||
|
import { MessageInstance } from '../interfaces';
|
||||||
|
|
||||||
export default function MessageInstanceList() {
|
export default function MessageInstanceList() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@ -17,6 +21,9 @@ export default function MessageInstanceList() {
|
|||||||
const [messageIntances, setMessageInstances] = useState([]);
|
const [messageIntances, setMessageInstances] = useState([]);
|
||||||
const [pagination, setPagination] = useState(null);
|
const [pagination, setPagination] = useState(null);
|
||||||
|
|
||||||
|
const [messageInstanceForModal, setMessageInstanceForModal] =
|
||||||
|
useState<MessageInstance | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const setMessageInstanceListFromResult = (result: any) => {
|
const setMessageInstanceListFromResult = (result: any) => {
|
||||||
setMessageInstances(result.results);
|
setMessageInstances(result.results);
|
||||||
@ -35,41 +42,89 @@ export default function MessageInstanceList() {
|
|||||||
});
|
});
|
||||||
}, [searchParams, params]);
|
}, [searchParams, params]);
|
||||||
|
|
||||||
const buildTable = () => {
|
const handleCorrelationDisplayClose = () => {
|
||||||
// return null;
|
setMessageInstanceForModal(null);
|
||||||
const rows = messageIntances.map((row) => {
|
};
|
||||||
const rowToUse = row as any;
|
|
||||||
|
const correlationsDisplayModal = () => {
|
||||||
|
if (messageInstanceForModal) {
|
||||||
|
let failureCausePre = null;
|
||||||
|
if (messageInstanceForModal.failure_cause) {
|
||||||
|
failureCausePre = (
|
||||||
|
<>
|
||||||
|
<p className="failure-string">
|
||||||
|
{messageInstanceForModal.failure_cause}
|
||||||
|
</p>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<tr key={rowToUse.id}>
|
<Modal
|
||||||
<td>{rowToUse.id}</td>
|
open={!!messageInstanceForModal}
|
||||||
<td>
|
passiveModal
|
||||||
<Link
|
onRequestClose={handleCorrelationDisplayClose}
|
||||||
data-qa="process-model-show-link"
|
modalHeading={`Message ${messageInstanceForModal.id} (${messageInstanceForModal.message_identifier}) ${messageInstanceForModal.message_type} data:`}
|
||||||
to={`/admin/process-models/${modifyProcessIdentifierForPathParam(
|
modalLabel="Details"
|
||||||
rowToUse.process_model_identifier
|
>
|
||||||
)}`}
|
{failureCausePre}
|
||||||
>
|
<p>Correlations:</p>
|
||||||
{rowToUse.process_model_identifier}
|
<pre>
|
||||||
</Link>
|
{JSON.stringify(
|
||||||
</td>
|
messageInstanceForModal.message_correlations,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildTable = () => {
|
||||||
|
const rows = messageIntances.map((row: MessageInstance) => {
|
||||||
|
let errorIcon = null;
|
||||||
|
let errorTitle = null;
|
||||||
|
if (row.failure_cause) {
|
||||||
|
errorTitle = 'Instance has an error';
|
||||||
|
errorIcon = (
|
||||||
|
<>
|
||||||
|
|
||||||
|
<ErrorOutline className="red-icon" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<tr key={row.id}>
|
||||||
|
<td>{row.id}</td>
|
||||||
|
<td>{FormatProcessModelDisplayName(row)}</td>
|
||||||
<td>
|
<td>
|
||||||
<Link
|
<Link
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifyProcessIdentifierForPathParam(
|
to={`/admin/process-instances/${modifyProcessIdentifierForPathParam(
|
||||||
rowToUse.process_model_identifier
|
row.process_model_identifier
|
||||||
)}/process-instances/${rowToUse.process_instance_id}`}
|
)}/${row.process_instance_id}`}
|
||||||
>
|
>
|
||||||
{rowToUse.process_instance_id}
|
{row.process_instance_id}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
<td>{rowToUse.message_identifier}</td>
|
<td>{row.message_identifier}</td>
|
||||||
<td>{rowToUse.message_type}</td>
|
<td>{row.message_type}</td>
|
||||||
<td>{rowToUse.failure_cause || '-'}</td>
|
|
||||||
<td>{rowToUse.status}</td>
|
|
||||||
<td>
|
<td>
|
||||||
{convertSecondsToFormattedDateString(
|
<Button
|
||||||
rowToUse.created_at_in_seconds
|
kind="ghost"
|
||||||
)}
|
className="button-link"
|
||||||
|
onClick={() => setMessageInstanceForModal(row)}
|
||||||
|
title={errorTitle}
|
||||||
|
>
|
||||||
|
View
|
||||||
|
{errorIcon}
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
<td>{row.status}</td>
|
||||||
|
<td>
|
||||||
|
{convertSecondsToFormattedDateTime(row.created_at_in_seconds)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@ -78,12 +133,12 @@ export default function MessageInstanceList() {
|
|||||||
<Table striped bordered>
|
<Table striped bordered>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Instance Id</th>
|
<th>Id</th>
|
||||||
<th>Process Model</th>
|
<th>Process</th>
|
||||||
<th>Process Instance</th>
|
<th>Process Instance</th>
|
||||||
<th>Message Model</th>
|
<th>Name</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Failure Cause</th>
|
<th>Details</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Created At</th>
|
<th>Created At</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -108,9 +163,9 @@ export default function MessageInstanceList() {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
`Process Instance: ${searchParams.get('process_instance_id')}`,
|
`Process Instance: ${searchParams.get('process_instance_id')}`,
|
||||||
`/admin/process-models/${searchParams.get(
|
`/admin/process-instances/${searchParams.get(
|
||||||
'process_model_id'
|
'process_model_id'
|
||||||
)}/process-instances/${searchParams.get('process_instance_id')}`,
|
)}/${searchParams.get('process_instance_id')}`,
|
||||||
],
|
],
|
||||||
['Messages'],
|
['Messages'],
|
||||||
]}
|
]}
|
||||||
@ -121,6 +176,7 @@ export default function MessageInstanceList() {
|
|||||||
<>
|
<>
|
||||||
{breadcrumbElement}
|
{breadcrumbElement}
|
||||||
<h1>Messages</h1>
|
<h1>Messages</h1>
|
||||||
|
{correlationsDisplayModal()}
|
||||||
<PaginationForTable
|
<PaginationForTable
|
||||||
page={page}
|
page={page}
|
||||||
perPage={perPage}
|
perPage={perPage}
|
||||||
|
@ -55,9 +55,9 @@ export default function MyTasks() {
|
|||||||
<p>
|
<p>
|
||||||
Process Instance {processInstance.id} kicked off (
|
Process Instance {processInstance.id} kicked off (
|
||||||
<Link
|
<Link
|
||||||
to={`/admin/process-models/${modifyProcessIdentifierForPathParam(
|
to={`/admin/process-instances/${modifyProcessIdentifierForPathParam(
|
||||||
processInstance.process_model_identifier
|
processInstance.process_model_identifier
|
||||||
)}/process-instances/${processInstance.id}`}
|
)}/${processInstance.id}`}
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
>
|
>
|
||||||
view
|
view
|
||||||
@ -95,7 +95,7 @@ export default function MyTasks() {
|
|||||||
<td>
|
<td>
|
||||||
<Link
|
<Link
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifiedProcessModelIdentifier}/process-instances/${rowToUse.process_instance_id}`}
|
to={`/admin/process-instances/${modifiedProcessModelIdentifier}/${rowToUse.process_instance_id}`}
|
||||||
>
|
>
|
||||||
{rowToUse.process_instance_id}
|
{rowToUse.process_instance_id}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Table } from '@carbon/react';
|
import { Table, Tabs, TabList, Tab } from '@carbon/react';
|
||||||
import { useParams, useSearchParams, Link } from 'react-router-dom';
|
import { useParams, useSearchParams, Link } from 'react-router-dom';
|
||||||
import PaginationForTable from '../components/PaginationForTable';
|
import PaginationForTable from '../components/PaginationForTable';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
@ -10,15 +10,18 @@ import {
|
|||||||
convertSecondsToFormattedDateTime,
|
convertSecondsToFormattedDateTime,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||||
|
|
||||||
export default function ProcessInstanceLogList() {
|
export default function ProcessInstanceLogList() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const [processInstanceLogs, setProcessInstanceLogs] = useState([]);
|
const [processInstanceLogs, setProcessInstanceLogs] = useState([]);
|
||||||
const [pagination, setPagination] = useState(null);
|
const [pagination, setPagination] = useState(null);
|
||||||
const modifiedProcessModelId = modifyProcessIdentifierForPathParam(
|
const modifiedProcessModelId = modifyProcessIdentifierForPathParam(
|
||||||
`${params.process_model_id}`
|
`${params.process_model_id}`
|
||||||
);
|
);
|
||||||
|
const { targetUris } = useUriListForPermissions();
|
||||||
|
const isDetailedView = searchParams.get('detailed') === 'true';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const setProcessInstanceLogListFromResult = (result: any) => {
|
const setProcessInstanceLogListFromResult = (result: any) => {
|
||||||
@ -27,26 +30,36 @@ export default function ProcessInstanceLogList() {
|
|||||||
};
|
};
|
||||||
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${params.process_instance_id}/logs?per_page=${perPage}&page=${page}`,
|
path: `${targetUris.processInstanceLogListPath}?per_page=${perPage}&page=${page}&detailed=${isDetailedView}`,
|
||||||
successCallback: setProcessInstanceLogListFromResult,
|
successCallback: setProcessInstanceLogListFromResult,
|
||||||
});
|
});
|
||||||
}, [searchParams, params]);
|
}, [
|
||||||
|
searchParams,
|
||||||
|
params,
|
||||||
|
targetUris.processInstanceLogListPath,
|
||||||
|
isDetailedView,
|
||||||
|
]);
|
||||||
|
|
||||||
const buildTable = () => {
|
const buildTable = () => {
|
||||||
const rows = processInstanceLogs.map((row) => {
|
const rows = processInstanceLogs.map((row) => {
|
||||||
const rowToUse = row as any;
|
const rowToUse = row as any;
|
||||||
return (
|
return (
|
||||||
<tr key={rowToUse.id}>
|
<tr key={rowToUse.id}>
|
||||||
<td>{rowToUse.bpmn_process_identifier}</td>
|
<td>{rowToUse.id}</td>
|
||||||
<td>{rowToUse.message}</td>
|
<td>{rowToUse.message}</td>
|
||||||
<td>{rowToUse.bpmn_task_identifier}</td>
|
|
||||||
<td>{rowToUse.bpmn_task_name}</td>
|
<td>{rowToUse.bpmn_task_name}</td>
|
||||||
<td>{rowToUse.bpmn_task_type}</td>
|
{isDetailedView && (
|
||||||
|
<>
|
||||||
|
<td>{rowToUse.bpmn_task_identifier}</td>
|
||||||
|
<td>{rowToUse.bpmn_task_type}</td>
|
||||||
|
<td>{rowToUse.bpmn_process_identifier}</td>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<td>{rowToUse.username}</td>
|
<td>{rowToUse.username}</td>
|
||||||
<td>
|
<td>
|
||||||
<Link
|
<Link
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`}
|
to={`/admin/process-instances/${modifiedProcessModelId}/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`}
|
||||||
>
|
>
|
||||||
{convertSecondsToFormattedDateTime(rowToUse.timestamp)}
|
{convertSecondsToFormattedDateTime(rowToUse.timestamp)}
|
||||||
</Link>
|
</Link>
|
||||||
@ -58,11 +71,16 @@ export default function ProcessInstanceLogList() {
|
|||||||
<Table size="lg">
|
<Table size="lg">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Bpmn Process Identifier</th>
|
<th>Id</th>
|
||||||
<th>Message</th>
|
<th>Message</th>
|
||||||
<th>Task Identifier</th>
|
|
||||||
<th>Task Name</th>
|
<th>Task Name</th>
|
||||||
<th>Task Type</th>
|
{isDetailedView && (
|
||||||
|
<>
|
||||||
|
<th>Task Identifier</th>
|
||||||
|
<th>Task Type</th>
|
||||||
|
<th>Bpmn Process Identifier</th>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
<th>Timestamp</th>
|
<th>Timestamp</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -71,11 +89,12 @@ export default function ProcessInstanceLogList() {
|
|||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
const selectedTabIndex = isDetailedView ? 1 : 0;
|
||||||
|
|
||||||
if (pagination) {
|
if (pagination) {
|
||||||
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
||||||
return (
|
return (
|
||||||
<main>
|
<>
|
||||||
<ProcessBreadcrumb
|
<ProcessBreadcrumb
|
||||||
hotCrumbs={[
|
hotCrumbs={[
|
||||||
['Process Groups', '/admin'],
|
['Process Groups', '/admin'],
|
||||||
@ -86,18 +105,41 @@ export default function ProcessInstanceLogList() {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
`Process Instance: ${params.process_instance_id}`,
|
`Process Instance: ${params.process_instance_id}`,
|
||||||
`/admin/process-models/${params.process_model_id}/process-instances/${params.process_instance_id}`,
|
`/admin/process-instances/${params.process_model_id}/${params.process_instance_id}`,
|
||||||
],
|
],
|
||||||
['Logs'],
|
['Logs'],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
<Tabs selectedIndex={selectedTabIndex}>
|
||||||
|
<TabList aria-label="List of tabs">
|
||||||
|
<Tab
|
||||||
|
title="Only show a subset of the logs, and show fewer columns"
|
||||||
|
onClick={() => {
|
||||||
|
searchParams.set('detailed', 'false');
|
||||||
|
setSearchParams(searchParams);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simple
|
||||||
|
</Tab>
|
||||||
|
<Tab
|
||||||
|
title="Show all logs for this process instance, and show extra columns that may be useful for debugging"
|
||||||
|
onClick={() => {
|
||||||
|
searchParams.set('detailed', 'true');
|
||||||
|
setSearchParams(searchParams);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Detailed
|
||||||
|
</Tab>
|
||||||
|
</TabList>
|
||||||
|
</Tabs>
|
||||||
|
<br />
|
||||||
<PaginationForTable
|
<PaginationForTable
|
||||||
page={page}
|
page={page}
|
||||||
perPage={perPage}
|
perPage={perPage}
|
||||||
pagination={pagination}
|
pagination={pagination}
|
||||||
tableToDisplay={buildTable()}
|
tableToDisplay={buildTable()}
|
||||||
/>
|
/>
|
||||||
</main>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -59,6 +59,11 @@ export default function ProcessInstanceShow() {
|
|||||||
const permissionRequestData: PermissionsToCheck = {
|
const permissionRequestData: PermissionsToCheck = {
|
||||||
[targetUris.messageInstanceListPath]: ['GET'],
|
[targetUris.messageInstanceListPath]: ['GET'],
|
||||||
[targetUris.processInstanceTaskListPath]: ['GET'],
|
[targetUris.processInstanceTaskListPath]: ['GET'],
|
||||||
|
[targetUris.processInstanceActionPath]: ['DELETE'],
|
||||||
|
[targetUris.processInstanceLogListPath]: ['GET'],
|
||||||
|
[`${targetUris.processInstanceActionPath}/suspend`]: ['PUT'],
|
||||||
|
[`${targetUris.processInstanceActionPath}/terminate`]: ['PUT'],
|
||||||
|
[`${targetUris.processInstanceActionPath}/resume`]: ['PUT'],
|
||||||
};
|
};
|
||||||
const { ability, permissionsLoaded } = usePermissionFetcher(
|
const { ability, permissionsLoaded } = usePermissionFetcher(
|
||||||
permissionRequestData
|
permissionRequestData
|
||||||
@ -76,7 +81,7 @@ export default function ProcessInstanceShow() {
|
|||||||
setTasksCallHadError(true);
|
setTasksCallHadError(true);
|
||||||
};
|
};
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}`,
|
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}`,
|
||||||
successCallback: setProcessInstance,
|
successCallback: setProcessInstance,
|
||||||
});
|
});
|
||||||
let taskParams = '?all_tasks=true';
|
let taskParams = '?all_tasks=true';
|
||||||
@ -85,7 +90,7 @@ export default function ProcessInstanceShow() {
|
|||||||
}
|
}
|
||||||
if (ability.can('GET', targetUris.processInstanceTaskListPath)) {
|
if (ability.can('GET', targetUris.processInstanceTaskListPath)) {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}/tasks${taskParams}`,
|
path: `${targetUris.processInstanceTaskListPath}${taskParams}`,
|
||||||
successCallback: setTasks,
|
successCallback: setTasks,
|
||||||
failureCallback: processTaskFailure,
|
failureCallback: processTaskFailure,
|
||||||
});
|
});
|
||||||
@ -97,7 +102,7 @@ export default function ProcessInstanceShow() {
|
|||||||
|
|
||||||
const deleteProcessInstance = () => {
|
const deleteProcessInstance = () => {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${params.process_instance_id}`,
|
path: targetUris.processInstanceActionPath,
|
||||||
successCallback: navigateToProcessInstances,
|
successCallback: navigateToProcessInstances,
|
||||||
httpMethod: 'DELETE',
|
httpMethod: 'DELETE',
|
||||||
});
|
});
|
||||||
@ -110,7 +115,7 @@ export default function ProcessInstanceShow() {
|
|||||||
|
|
||||||
const terminateProcessInstance = () => {
|
const terminateProcessInstance = () => {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${params.process_instance_id}/terminate`,
|
path: `${targetUris.processInstanceActionPath}/terminate`,
|
||||||
successCallback: refreshPage,
|
successCallback: refreshPage,
|
||||||
httpMethod: 'POST',
|
httpMethod: 'POST',
|
||||||
});
|
});
|
||||||
@ -118,7 +123,7 @@ export default function ProcessInstanceShow() {
|
|||||||
|
|
||||||
const suspendProcessInstance = () => {
|
const suspendProcessInstance = () => {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${params.process_instance_id}/suspend`,
|
path: `${targetUris.processInstanceActionPath}/suspend`,
|
||||||
successCallback: refreshPage,
|
successCallback: refreshPage,
|
||||||
httpMethod: 'POST',
|
httpMethod: 'POST',
|
||||||
});
|
});
|
||||||
@ -126,7 +131,7 @@ export default function ProcessInstanceShow() {
|
|||||||
|
|
||||||
const resumeProcessInstance = () => {
|
const resumeProcessInstance = () => {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${params.process_instance_id}/resume`,
|
path: `${targetUris.processInstanceActionPath}/resume`,
|
||||||
successCallback: refreshPage,
|
successCallback: refreshPage,
|
||||||
httpMethod: 'POST',
|
httpMethod: 'POST',
|
||||||
});
|
});
|
||||||
@ -174,7 +179,7 @@ export default function ProcessInstanceShow() {
|
|||||||
<Link
|
<Link
|
||||||
reloadDocument
|
reloadDocument
|
||||||
data-qa="process-instance-step-link"
|
data-qa="process-instance-step-link"
|
||||||
to={`/admin/process-models/${
|
to={`/admin/process-instances/${
|
||||||
params.process_model_id
|
params.process_model_id
|
||||||
}/process-instances/${params.process_instance_id}/${
|
}/process-instances/${params.process_instance_id}/${
|
||||||
currentSpiffStep(processInstanceToUse) + distance
|
currentSpiffStep(processInstanceToUse) + distance
|
||||||
@ -209,7 +214,7 @@ export default function ProcessInstanceShow() {
|
|||||||
if (currentEndDate) {
|
if (currentEndDate) {
|
||||||
currentEndDateTag = (
|
currentEndDateTag = (
|
||||||
<Grid condensed fullWidth>
|
<Grid condensed fullWidth>
|
||||||
<Column sm={1} md={1} lg={1} className="grid-list-title">
|
<Column sm={1} md={1} lg={2} className="grid-list-title">
|
||||||
Completed:{' '}
|
Completed:{' '}
|
||||||
</Column>
|
</Column>
|
||||||
<Column sm={3} md={3} lg={3} className="grid-date">
|
<Column sm={3} md={3} lg={3} className="grid-date">
|
||||||
@ -235,7 +240,7 @@ export default function ProcessInstanceShow() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid condensed fullWidth>
|
<Grid condensed fullWidth>
|
||||||
<Column sm={1} md={1} lg={1} className="grid-list-title">
|
<Column sm={1} md={1} lg={2} className="grid-list-title">
|
||||||
Started:{' '}
|
Started:{' '}
|
||||||
</Column>
|
</Column>
|
||||||
<Column sm={3} md={3} lg={3} className="grid-date">
|
<Column sm={3} md={3} lg={3} className="grid-date">
|
||||||
@ -246,7 +251,7 @@ export default function ProcessInstanceShow() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
{currentEndDateTag}
|
{currentEndDateTag}
|
||||||
<Grid condensed fullWidth>
|
<Grid condensed fullWidth>
|
||||||
<Column sm={1} md={1} lg={1} className="grid-list-title">
|
<Column sm={1} md={1} lg={2} className="grid-list-title">
|
||||||
Status:{' '}
|
Status:{' '}
|
||||||
</Column>
|
</Column>
|
||||||
<Column sm={3} md={3} lg={3}>
|
<Column sm={3} md={3} lg={3}>
|
||||||
@ -259,14 +264,20 @@ export default function ProcessInstanceShow() {
|
|||||||
<Grid condensed fullWidth>
|
<Grid condensed fullWidth>
|
||||||
<Column sm={2} md={2} lg={2}>
|
<Column sm={2} md={2} lg={2}>
|
||||||
<ButtonSet>
|
<ButtonSet>
|
||||||
<Button
|
<Can
|
||||||
size="sm"
|
I="GET"
|
||||||
className="button-white-background"
|
a={targetUris.processInstanceLogListPath}
|
||||||
data-qa="process-instance-log-list-link"
|
ability={ability}
|
||||||
href={`/admin/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}/logs`}
|
|
||||||
>
|
>
|
||||||
Logs
|
<Button
|
||||||
</Button>
|
size="sm"
|
||||||
|
className="button-white-background"
|
||||||
|
data-qa="process-instance-log-list-link"
|
||||||
|
href={`/admin/logs/${modifiedProcessModelId}/${params.process_instance_id}`}
|
||||||
|
>
|
||||||
|
Logs
|
||||||
|
</Button>
|
||||||
|
</Can>
|
||||||
<Can
|
<Can
|
||||||
I="GET"
|
I="GET"
|
||||||
a={targetUris.messageInstanceListPath}
|
a={targetUris.messageInstanceListPath}
|
||||||
@ -436,8 +447,8 @@ export default function ProcessInstanceShow() {
|
|||||||
// taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute
|
// taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute
|
||||||
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };
|
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/process-instances/${params.process_instance_id}/task/${taskToUse.id}/update`,
|
path: `/task-data/${modifiedProcessModelId}/${params.process_instance_id}/${taskToUse.id}`,
|
||||||
httpMethod: 'POST',
|
httpMethod: 'PUT',
|
||||||
successCallback: saveTaskDataResult,
|
successCallback: saveTaskDataResult,
|
||||||
failureCallback: saveTaskDataFailure,
|
failureCallback: saveTaskDataFailure,
|
||||||
postBody: {
|
postBody: {
|
||||||
@ -544,21 +555,33 @@ export default function ProcessInstanceShow() {
|
|||||||
|
|
||||||
const buttonIcons = (processInstanceToUse: any) => {
|
const buttonIcons = (processInstanceToUse: any) => {
|
||||||
const elements = [];
|
const elements = [];
|
||||||
elements.push(terminateButton(processInstanceToUse));
|
if (
|
||||||
elements.push(suspendButton(processInstanceToUse));
|
ability.can('POST', `${targetUris.processInstanceActionPath}/terminate`)
|
||||||
elements.push(resumeButton(processInstanceToUse));
|
) {
|
||||||
elements.push(
|
elements.push(terminateButton(processInstanceToUse));
|
||||||
<ButtonWithConfirmation
|
}
|
||||||
data-qa="process-instance-delete"
|
if (
|
||||||
kind="ghost"
|
ability.can('POST', `${targetUris.processInstanceActionPath}/suspend`)
|
||||||
renderIcon={TrashCan}
|
) {
|
||||||
iconDescription="Delete"
|
elements.push(suspendButton(processInstanceToUse));
|
||||||
hasIconOnly
|
}
|
||||||
description={`Delete Process Instance: ${processInstanceToUse.id}`}
|
if (ability.can('POST', `${targetUris.processInstanceActionPath}/resume`)) {
|
||||||
onConfirmation={deleteProcessInstance}
|
elements.push(resumeButton(processInstanceToUse));
|
||||||
confirmButtonLabel="Delete"
|
}
|
||||||
/>
|
if (ability.can('DELETE', targetUris.processInstanceActionPath)) {
|
||||||
);
|
elements.push(
|
||||||
|
<ButtonWithConfirmation
|
||||||
|
data-qa="process-instance-delete"
|
||||||
|
kind="ghost"
|
||||||
|
renderIcon={TrashCan}
|
||||||
|
iconDescription="Delete"
|
||||||
|
hasIconOnly
|
||||||
|
description={`Delete Process Instance: ${processInstanceToUse.id}`}
|
||||||
|
onConfirmation={deleteProcessInstance}
|
||||||
|
confirmButtonLabel="Delete"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
return elements;
|
return elements;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ export default function ProcessModelEditDiagram() {
|
|||||||
|
|
||||||
const onServiceTasksRequested = (event: any) => {
|
const onServiceTasksRequested = (event: any) => {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/service_tasks`,
|
path: `/service-tasks`,
|
||||||
successCallback: makeApiHandler(event),
|
successCallback: makeApiHandler(event),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -735,7 +735,7 @@ export default function ProcessModelEditDiagram() {
|
|||||||
if (processModel) {
|
if (processModel) {
|
||||||
const files = processModel.files.filter((f) => f.type === type);
|
const files = processModel.files.filter((f) => f.type === type);
|
||||||
files.some((file) => {
|
files.some((file) => {
|
||||||
if (file.references.some((ref) => ref.id === id)) {
|
if (file.references.some((ref) => ref.identifier === id)) {
|
||||||
matchFile = file;
|
matchFile = file;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ export default function ProcessModelShow() {
|
|||||||
const permissionRequestData: PermissionsToCheck = {
|
const permissionRequestData: PermissionsToCheck = {
|
||||||
[targetUris.processModelShowPath]: ['PUT', 'DELETE'],
|
[targetUris.processModelShowPath]: ['PUT', 'DELETE'],
|
||||||
[targetUris.processInstanceListPath]: ['GET'],
|
[targetUris.processInstanceListPath]: ['GET'],
|
||||||
[targetUris.processInstanceActionPath]: ['POST'],
|
[targetUris.processInstanceCreatePath]: ['POST'],
|
||||||
[targetUris.processModelFileCreatePath]: ['POST', 'PUT', 'GET', 'DELETE'],
|
[targetUris.processModelFileCreatePath]: ['POST', 'PUT', 'GET', 'DELETE'],
|
||||||
};
|
};
|
||||||
const { ability, permissionsLoaded } = usePermissionFetcher(
|
const { ability, permissionsLoaded } = usePermissionFetcher(
|
||||||
@ -95,7 +95,7 @@ export default function ProcessModelShow() {
|
|||||||
<p>
|
<p>
|
||||||
Process Instance {processInstance.id} kicked off (
|
Process Instance {processInstance.id} kicked off (
|
||||||
<Link
|
<Link
|
||||||
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${processInstance.id}`}
|
to={`/admin/process-instances/${modifiedProcessModelId}/${processInstance.id}`}
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
>
|
>
|
||||||
view
|
view
|
||||||
@ -556,7 +556,7 @@ export default function ProcessModelShow() {
|
|||||||
<Stack orientation="horizontal" gap={3}>
|
<Stack orientation="horizontal" gap={3}>
|
||||||
<Can
|
<Can
|
||||||
I="POST"
|
I="POST"
|
||||||
a={targetUris.processInstanceActionPath}
|
a={targetUris.processInstanceCreatePath}
|
||||||
ability={ability}
|
ability={ability}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
|
@ -26,6 +26,9 @@ import Form from '../themes/carbon';
|
|||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
import ErrorContext from '../contexts/ErrorContext';
|
import ErrorContext from '../contexts/ErrorContext';
|
||||||
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
import { modifyProcessIdentifierForPathParam } from '../helpers';
|
||||||
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||||
|
import { PermissionsToCheck } from '../interfaces';
|
||||||
|
import { usePermissionFetcher } from '../hooks/PermissionService';
|
||||||
|
|
||||||
export default function TaskShow() {
|
export default function TaskShow() {
|
||||||
const [task, setTask] = useState(null);
|
const [task, setTask] = useState(null);
|
||||||
@ -35,24 +38,36 @@ export default function TaskShow() {
|
|||||||
|
|
||||||
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||||
|
|
||||||
useEffect(() => {
|
const { targetUris } = useUriListForPermissions();
|
||||||
const processResult = (result: any) => {
|
const permissionRequestData: PermissionsToCheck = {
|
||||||
setTask(result);
|
[targetUris.processInstanceTaskListPath]: ['GET'],
|
||||||
HttpService.makeCallToBackend({
|
};
|
||||||
path: `/process-instances/${modifyProcessIdentifierForPathParam(
|
const { ability, permissionsLoaded } = usePermissionFetcher(
|
||||||
result.process_model_identifier
|
permissionRequestData
|
||||||
)}/${params.process_instance_id}/tasks`,
|
);
|
||||||
successCallback: setUserTasks,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
HttpService.makeCallToBackend({
|
useEffect(() => {
|
||||||
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
if (permissionsLoaded) {
|
||||||
successCallback: processResult,
|
const processResult = (result: any) => {
|
||||||
// This causes the page to continuously reload
|
setTask(result);
|
||||||
// failureCallback: setErrorMessage,
|
if (ability.can('GET', targetUris.processInstanceTaskListPath)) {
|
||||||
});
|
HttpService.makeCallToBackend({
|
||||||
}, [params]);
|
path: `/task-data/${modifyProcessIdentifierForPathParam(
|
||||||
|
result.process_model_identifier
|
||||||
|
)}/${params.process_instance_id}`,
|
||||||
|
successCallback: setUserTasks,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/tasks/${params.process_instance_id}/${params.task_id}`,
|
||||||
|
successCallback: processResult,
|
||||||
|
// This causes the page to continuously reload
|
||||||
|
// failureCallback: setErrorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [params, permissionsLoaded, ability, targetUris]);
|
||||||
|
|
||||||
const processSubmitResult = (result: any) => {
|
const processSubmitResult = (result: any) => {
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
@ -116,17 +131,18 @@ export default function TaskShow() {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
title="Steps in this process instance involving people"
|
||||||
|
selectedIndex={selectedTabIndex}
|
||||||
|
>
|
||||||
|
<TabList aria-label="List of tabs" contained>
|
||||||
|
{userTasksElement}
|
||||||
|
</TabList>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return null;
|
||||||
<Tabs
|
|
||||||
title="Steps in this process instance involving people"
|
|
||||||
selectedIndex={selectedTabIndex}
|
|
||||||
>
|
|
||||||
<TabList aria-label="List of tabs" contained>
|
|
||||||
{userTasksElement}
|
|
||||||
</TabList>
|
|
||||||
</Tabs>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const formElement = (taskToUse: any) => {
|
const formElement = (taskToUse: any) => {
|
||||||
@ -207,7 +223,7 @@ export default function TaskShow() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (task && userTasks) {
|
if (task) {
|
||||||
const taskToUse = task as any;
|
const taskToUse = task as any;
|
||||||
let statusString = '';
|
let statusString = '';
|
||||||
if (taskToUse.state !== 'READY') {
|
if (taskToUse.state !== 'READY') {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user