added ability to view data objects from the process instance show page w/ burnettk
This commit is contained in:
parent
f6c5c005d9
commit
866346f47b
|
@ -1,5 +1,4 @@
|
|||
"""Get the bpmn process json for a given process instance id and store it in /tmp."""
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
@ -18,15 +17,17 @@ def main(process_instance_id: str):
|
|||
id=process_instance_id
|
||||
).first()
|
||||
|
||||
file_path = f"/tmp/{process_instance_id}_bpmn_json.json"
|
||||
if not process_instance:
|
||||
raise Exception(
|
||||
f"Could not find a process instance with id: {process_instance_id}"
|
||||
)
|
||||
|
||||
with open(
|
||||
f"/tmp/{process_instance_id}_bpmn_json.json", "w", encoding="utf-8"
|
||||
file_path, "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write(process_instance.bpmn_json)
|
||||
print(f"Saved to {file_path}")
|
||||
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
|
|
|
@ -174,7 +174,7 @@ paths:
|
|||
items:
|
||||
$ref: "#/components/schemas/ProcessModelCategory"
|
||||
post:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_add
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_create
|
||||
summary: Add process group
|
||||
tags:
|
||||
- Process Groups
|
||||
|
@ -1439,7 +1439,7 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
put:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.update_task_data
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.task_data_update
|
||||
summary: Update the task data for requested instance and task
|
||||
tags:
|
||||
- Process Instances
|
||||
|
@ -1451,6 +1451,39 @@ paths:
|
|||
schema:
|
||||
$ref: "#/components/schemas/Workflow"
|
||||
|
||||
/process-data/{modified_process_model_identifier}/{process_instance_id}/{process_data_identifier}:
|
||||
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: process_data_identifier
|
||||
in: path
|
||||
required: true
|
||||
description: The identifier of the process data.
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_data_show
|
||||
summary: Fetch the process data value.
|
||||
tags:
|
||||
- Data Objects
|
||||
responses:
|
||||
"200":
|
||||
description: Fetch succeeded.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Workflow"
|
||||
|
||||
/service-tasks:
|
||||
get:
|
||||
tags:
|
||||
|
@ -1689,7 +1722,7 @@ paths:
|
|||
schema:
|
||||
type: integer
|
||||
post:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.add_secret
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.secret_create
|
||||
summary: Create a secret for a key and value
|
||||
tags:
|
||||
- Secrets
|
||||
|
@ -1739,7 +1772,7 @@ paths:
|
|||
schema:
|
||||
$ref: "#/components/schemas/Secret"
|
||||
delete:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.delete_secret
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.secret_delete
|
||||
summary: Delete an existing secret
|
||||
tags:
|
||||
- Secrets
|
||||
|
@ -1751,7 +1784,7 @@ paths:
|
|||
"404":
|
||||
description: Secret does not exist
|
||||
put:
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.update_secret
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.secret_update
|
||||
summary: Modify an existing secret
|
||||
tags:
|
||||
- Secrets
|
||||
|
|
|
@ -158,17 +158,12 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R
|
|||
return make_response(jsonify({"results": response_dict}), 200)
|
||||
|
||||
|
||||
def modify_process_model_id(process_model_id: str) -> str:
|
||||
"""Modify_process_model_id."""
|
||||
return process_model_id.replace("/", ":")
|
||||
|
||||
|
||||
def un_modify_modified_process_model_id(modified_process_model_identifier: str) -> str:
|
||||
"""Un_modify_modified_process_model_id."""
|
||||
return modified_process_model_identifier.replace(":", "/")
|
||||
|
||||
|
||||
def process_group_add(body: dict) -> flask.wrappers.Response:
|
||||
def process_group_create(body: dict) -> flask.wrappers.Response:
|
||||
"""Add_process_group."""
|
||||
process_group = ProcessGroup(**body)
|
||||
ProcessModelService.add_process_group(process_group)
|
||||
|
@ -1354,7 +1349,6 @@ def process_instance_task_list_without_task_data_for_me(
|
|||
) -> flask.wrappers.Response:
|
||||
"""Process_instance_task_list_without_task_data_for_me."""
|
||||
process_instance = _find_process_instance_for_me_or_raise(process_instance_id)
|
||||
print(f"process_instance: {process_instance}")
|
||||
return process_instance_task_list(
|
||||
modified_process_model_identifier,
|
||||
process_instance,
|
||||
|
@ -1540,6 +1534,30 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
|
|||
return make_response(jsonify(task), 200)
|
||||
|
||||
|
||||
def process_data_show(
|
||||
process_instance_id: int,
|
||||
process_data_identifier: str,
|
||||
modified_process_model_identifier: str,
|
||||
) -> flask.wrappers.Response:
|
||||
"""Process_data_show."""
|
||||
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
all_process_data = processor.get_data()
|
||||
process_data_value = None
|
||||
if process_data_identifier in all_process_data:
|
||||
process_data_value = all_process_data[process_data_identifier]
|
||||
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"process_data_identifier": process_data_identifier,
|
||||
"process_data_value": process_data_value,
|
||||
}
|
||||
),
|
||||
200,
|
||||
)
|
||||
|
||||
|
||||
def task_submit(
|
||||
process_instance_id: int,
|
||||
task_id: str,
|
||||
|
@ -1907,7 +1925,7 @@ def secret_list(
|
|||
return make_response(jsonify(response_json), 200)
|
||||
|
||||
|
||||
def add_secret(body: Dict) -> Response:
|
||||
def secret_create(body: Dict) -> Response:
|
||||
"""Add secret."""
|
||||
secret_model = SecretService().add_secret(body["key"], body["value"], g.user.id)
|
||||
return Response(
|
||||
|
@ -1917,20 +1935,20 @@ def add_secret(body: Dict) -> Response:
|
|||
)
|
||||
|
||||
|
||||
def update_secret(key: str, body: dict) -> Response:
|
||||
def secret_update(key: str, body: dict) -> Response:
|
||||
"""Update secret."""
|
||||
SecretService().update_secret(key, body["value"], g.user.id)
|
||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
||||
|
||||
|
||||
def delete_secret(key: str) -> Response:
|
||||
def secret_delete(key: str) -> Response:
|
||||
"""Delete secret."""
|
||||
current_user = UserService.current_user()
|
||||
SecretService.delete_secret(key, current_user.id)
|
||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
||||
|
||||
|
||||
def update_task_data(
|
||||
def task_data_update(
|
||||
process_instance_id: str,
|
||||
modified_process_model_identifier: str,
|
||||
task_id: str,
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?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_hjecbuk" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0hnphp9</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0hnphp9" sourceRef="StartEvent_1" targetRef="Activity_16lbvwu" />
|
||||
<bpmn:scriptTask id="Activity_16lbvwu">
|
||||
<bpmn:incoming>Flow_0hnphp9</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0amajxh</bpmn:outgoing>
|
||||
<bpmn:dataOutputAssociation id="DataOutputAssociation_15x55ya">
|
||||
<bpmn:targetRef>DataObjectReference_10g8dit</bpmn:targetRef>
|
||||
</bpmn:dataOutputAssociation>
|
||||
<bpmn:script>the_data_object_var = 'hey'</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_0amajxh" sourceRef="Activity_16lbvwu" targetRef="manual_task" />
|
||||
<bpmn:endEvent id="Event_0ik0i72">
|
||||
<bpmn:incoming>Flow_1ifqo6o</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1ifqo6o" sourceRef="manual_task" targetRef="Event_0ik0i72" />
|
||||
<bpmn:manualTask id="manual_task">
|
||||
<bpmn:incoming>Flow_0amajxh</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1ifqo6o</bpmn:outgoing>
|
||||
<bpmn:property id="Property_0a8w16m" name="__targetRef_placeholder" />
|
||||
<bpmn:dataInputAssociation id="DataInputAssociation_0iqtpwy">
|
||||
<bpmn:sourceRef>DataObjectReference_10g8dit</bpmn:sourceRef>
|
||||
<bpmn:targetRef>Property_0a8w16m</bpmn:targetRef>
|
||||
</bpmn:dataInputAssociation>
|
||||
</bpmn:manualTask>
|
||||
<bpmn:dataObjectReference id="DataObjectReference_10g8dit" name="The Data Object Var" dataObjectRef="the_data_object_var" />
|
||||
<bpmn:dataObject id="the_data_object_var" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_hjecbuk">
|
||||
<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_0wqvy5h_di" bpmnElement="Activity_16lbvwu">
|
||||
<dc:Bounds x="290" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0ik0i72_di" bpmnElement="Event_0ik0i72">
|
||||
<dc:Bounds x="652" y="159" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0keslpp_di" bpmnElement="manual_task">
|
||||
<dc:Bounds x="470" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0hnphp9_di" bpmnElement="Flow_0hnphp9">
|
||||
<di:waypoint x="215" y="177" />
|
||||
<di:waypoint x="290" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0amajxh_di" bpmnElement="Flow_0amajxh">
|
||||
<di:waypoint x="390" y="177" />
|
||||
<di:waypoint x="470" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1ifqo6o_di" bpmnElement="Flow_1ifqo6o">
|
||||
<di:waypoint x="570" y="177" />
|
||||
<di:waypoint x="652" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="DataObjectReference_10g8dit_di" bpmnElement="DataObjectReference_10g8dit">
|
||||
<dc:Bounds x="412" y="275" width="36" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="390" y="332" width="81" height="27" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="DataInputAssociation_0iqtpwy_di" bpmnElement="DataInputAssociation_0iqtpwy">
|
||||
<di:waypoint x="448" y="275" />
|
||||
<di:waypoint x="491" y="217" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="DataOutputAssociation_15x55ya_di" bpmnElement="DataOutputAssociation_15x55ya">
|
||||
<di:waypoint x="371" y="217" />
|
||||
<di:waypoint x="416" y="275" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -2928,3 +2928,31 @@ class TestProcessApi(BaseTest):
|
|||
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
|
||||
|
||||
def test_process_data_show(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test_process_data_show."""
|
||||
process_model = load_test_spec(
|
||||
"test_group/data_object_test",
|
||||
process_model_source_directory="data_object_test",
|
||||
)
|
||||
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 == "user_input_required"
|
||||
|
||||
response = client.get(
|
||||
f"/v1.0/process-data/{self.modify_process_identifier_for_path_param(process_model.id)}/{process_instance_one.id}/the_data_object_var",
|
||||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json is not None
|
||||
assert response.json["process_data_value"] == "hey"
|
||||
|
|
|
@ -5,6 +5,11 @@ export interface Secret {
|
|||
creator_user_id: string;
|
||||
}
|
||||
|
||||
export interface ProcessData {
|
||||
process_data_identifier: string;
|
||||
process_data_value: any;
|
||||
}
|
||||
|
||||
export interface RecentProcessModel {
|
||||
processGroupIdentifier?: string;
|
||||
processModelIdentifier: string;
|
||||
|
|
|
@ -41,6 +41,7 @@ import ErrorContext from '../contexts/ErrorContext';
|
|||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||
import {
|
||||
PermissionsToCheck,
|
||||
ProcessData,
|
||||
ProcessInstance,
|
||||
ProcessInstanceTask,
|
||||
} from '../interfaces';
|
||||
|
@ -62,6 +63,8 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
const [tasksCallHadError, setTasksCallHadError] = useState<boolean>(false);
|
||||
const [taskToDisplay, setTaskToDisplay] = useState<object | null>(null);
|
||||
const [taskDataToDisplay, setTaskDataToDisplay] = useState<string>('');
|
||||
const [processDataToDisplay, setProcessDataToDisplay] =
|
||||
useState<ProcessData | null>(null);
|
||||
const [editingTaskData, setEditingTaskData] = useState<boolean>(false);
|
||||
|
||||
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||
|
@ -78,15 +81,15 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
: targetUris.processInstanceTaskListForMePath;
|
||||
|
||||
const permissionRequestData: PermissionsToCheck = {
|
||||
[targetUris.messageInstanceListPath]: ['GET'],
|
||||
[taskListPath]: ['GET'],
|
||||
[targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'],
|
||||
[targetUris.processInstanceActionPath]: ['DELETE'],
|
||||
[targetUris.processInstanceLogListPath]: ['GET'],
|
||||
[targetUris.processModelShowPath]: ['PUT'],
|
||||
[`${targetUris.processInstanceResumePath}`]: ['POST'],
|
||||
[`${targetUris.processInstanceSuspendPath}`]: ['POST'],
|
||||
[`${targetUris.processInstanceTerminatePath}`]: ['POST'],
|
||||
[targetUris.messageInstanceListPath]: ['GET'],
|
||||
[targetUris.processInstanceActionPath]: ['DELETE'],
|
||||
[targetUris.processInstanceLogListPath]: ['GET'],
|
||||
[targetUris.processInstanceTaskListDataPath]: ['GET', 'PUT'],
|
||||
[targetUris.processModelShowPath]: ['PUT'],
|
||||
[taskListPath]: ['GET'],
|
||||
};
|
||||
const { ability, permissionsLoaded } = usePermissionFetcher(
|
||||
permissionRequestData
|
||||
|
@ -411,16 +414,50 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
}
|
||||
};
|
||||
|
||||
const handleProcessDataDisplayClose = () => {
|
||||
setProcessDataToDisplay(null);
|
||||
};
|
||||
|
||||
const processDataDisplayArea = () => {
|
||||
if (processDataToDisplay) {
|
||||
return (
|
||||
<Modal
|
||||
open={!!processDataToDisplay}
|
||||
passiveModal
|
||||
onRequestClose={handleProcessDataDisplayClose}
|
||||
>
|
||||
<h2>Data Object: {processDataToDisplay.process_data_identifier}</h2>
|
||||
<br />
|
||||
<p>Value:</p>
|
||||
<pre>{JSON.stringify(processDataToDisplay.process_data_value)}</pre>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const handleProcessDataShowResponse = (processData: ProcessData) => {
|
||||
setProcessDataToDisplay(processData);
|
||||
};
|
||||
|
||||
const handleClickedDiagramTask = (
|
||||
shapeElement: any,
|
||||
bpmnProcessIdentifiers: any
|
||||
) => {
|
||||
if (tasks) {
|
||||
const matchingTask: any = tasks.find(
|
||||
(task: any) =>
|
||||
if (shapeElement.type === 'bpmn:DataObjectReference') {
|
||||
const dataObjectIdentifer = shapeElement.businessObject.dataObjectRef.id;
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-data/${params.process_model_id}/${params.process_instance_id}/${dataObjectIdentifer}`,
|
||||
httpMethod: 'GET',
|
||||
successCallback: handleProcessDataShowResponse,
|
||||
});
|
||||
} else if (tasks) {
|
||||
const matchingTask: any = tasks.find((task: any) => {
|
||||
return (
|
||||
task.name === shapeElement.id &&
|
||||
bpmnProcessIdentifiers.includes(task.process_identifier)
|
||||
);
|
||||
});
|
||||
if (matchingTask) {
|
||||
setTaskToDisplay(matchingTask);
|
||||
initializeTaskDataToDisplay(matchingTask);
|
||||
|
@ -503,7 +540,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
// taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute
|
||||
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/task-data/${modifiedProcessModelId}/${params.process_instance_id}/${taskToUse.id}`,
|
||||
path: `${targetUris.processInstanceTaskListDataPath}/${taskToUse.id}`,
|
||||
httpMethod: 'PUT',
|
||||
successCallback: saveTaskDataResult,
|
||||
failureCallback: saveTaskDataFailure,
|
||||
|
@ -687,6 +724,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
{getInfoTag()}
|
||||
<br />
|
||||
{taskDataDisplayArea()}
|
||||
{processDataDisplayArea()}
|
||||
{stepsElement()}
|
||||
<br />
|
||||
<ReactDiagramEditor
|
||||
|
|
Loading…
Reference in New Issue