diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py
index 5c924196..e1851773 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/task.py
@@ -115,8 +115,8 @@ class Task:
process_model_display_name: Union[str, None] = None,
process_group_identifier: Union[str, None] = None,
process_model_identifier: Union[str, None] = None,
- form_schema: Union[str, None] = None,
- form_ui_schema: Union[str, None] = None,
+ form_schema: Union[dict, None] = None,
+ form_ui_schema: Union[dict, None] = None,
parent: Optional[str] = None,
event_definition: Union[dict[str, Any], None] = None,
call_activity_process_identifier: Optional[str] = None,
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
index 6f272287..79cd8424 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py
@@ -170,6 +170,25 @@ def task_list_for_my_groups(
)
+def _munge_form_ui_schema_based_on_hidden_fields_in_task_data(task: Task) -> None:
+ if task.form_ui_schema is None:
+ task.form_ui_schema = {}
+
+ if task.data and "form_ui_hidden_fields" in task.data:
+ hidden_fields = task.data["form_ui_hidden_fields"]
+ for hidden_field in hidden_fields:
+ hidden_field_parts = hidden_field.split(".")
+ relevant_depth_of_ui_schema = task.form_ui_schema
+ for ii, hidden_field_part in enumerate(hidden_field_parts):
+ if hidden_field_part not in relevant_depth_of_ui_schema:
+ relevant_depth_of_ui_schema[hidden_field_part] = {}
+ relevant_depth_of_ui_schema = relevant_depth_of_ui_schema[
+ hidden_field_part
+ ]
+ if len(hidden_field_parts) == ii + 1:
+ relevant_depth_of_ui_schema["ui:widget"] = "hidden"
+
+
def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response:
"""Task_show."""
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
@@ -185,20 +204,7 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
process_instance.process_model_identifier,
)
- human_task = HumanTaskModel.query.filter_by(
- process_instance_id=process_instance_id, task_id=task_id
- ).first()
- if human_task is None:
- raise (
- ApiError(
- error_code="no_human_task",
- message=(
- f"Cannot find a task to complete for task id '{task_id}' and"
- f" process instance {process_instance_id}."
- ),
- status_code=500,
- )
- )
+ _find_human_task_or_raise(process_instance_id, task_id)
form_schema_file_name = ""
form_ui_schema_file_name = ""
@@ -253,31 +259,16 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
)
)
- form_contents = _prepare_form_data(
+ form_dict = _prepare_form_data(
form_schema_file_name,
spiff_task,
process_model_with_form,
)
- try:
- # form_contents is a str
- form_dict = json.loads(form_contents)
- except Exception as exception:
- raise (
- ApiError(
- error_code="error_loading_form",
- message=(
- f"Could not load form schema from: {form_schema_file_name}."
- f" Error was: {str(exception)}"
- ),
- status_code=400,
- )
- ) from exception
-
if task.data:
_update_form_schema_with_task_data_as_needed(form_dict, task, spiff_task)
- if form_contents:
+ if form_dict:
task.form_schema = form_dict
if form_ui_schema_file_name:
@@ -289,6 +280,8 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
if ui_form_contents:
task.form_ui_schema = ui_form_contents
+ _munge_form_ui_schema_based_on_hidden_fields_in_task_data(task)
+
if task.properties and task.data and "instructionsForEndUser" in task.properties:
if task.properties["instructionsForEndUser"]:
try:
@@ -365,20 +358,11 @@ def task_submit_shared(
if terminate_loop and spiff_task.is_looping():
spiff_task.terminate_loop()
- human_task = HumanTaskModel.query.filter_by(
- process_instance_id=process_instance_id, task_id=task_id, completed=False
- ).first()
- if human_task is None:
- raise (
- ApiError(
- error_code="no_human_task",
- message=(
- f"Cannot find a task to complete for task id '{task_id}' and"
- f" process instance {process_instance_id}."
- ),
- status_code=500,
- )
- )
+ human_task = _find_human_task_or_raise(
+ process_instance_id=process_instance_id,
+ task_id=task_id,
+ only_tasks_that_can_be_completed=True,
+ )
with sentry_sdk.start_span(op="task", description="complete_form_task"):
processor.lock_process_instance("Web")
@@ -525,14 +509,29 @@ def _get_tasks(
def _prepare_form_data(
form_file: str, spiff_task: SpiffTask, process_model: ProcessModelInfo
-) -> str:
+) -> dict:
"""Prepare_form_data."""
if spiff_task.data is None:
- return ""
+ return {}
file_contents = SpecFileService.get_data(process_model, form_file).decode("utf-8")
try:
- return _render_jinja_template(file_contents, spiff_task)
+ form_contents = _render_jinja_template(file_contents, spiff_task)
+ try:
+ # form_contents is a str
+ hot_dict: dict = json.loads(form_contents)
+ return hot_dict
+ except Exception as exception:
+ raise (
+ ApiError(
+ error_code="error_loading_form",
+ message=(
+ f"Could not load form schema from: {form_file}."
+ f" Error was: {str(exception)}"
+ ),
+ status_code=400,
+ )
+ ) from exception
except WorkflowTaskException as wfe:
wfe.add_note(f"Error in Json Form File '{form_file}'")
api_error = ApiError.from_workflow_exception(
@@ -670,3 +669,32 @@ def _get_potential_owner_usernames(assigned_user: AliasedClass) -> Any:
).label("potential_owner_usernames")
return potential_owner_usernames_from_group_concat_or_similar
+
+
+def _find_human_task_or_raise(
+ process_instance_id: int,
+ task_id: str,
+ only_tasks_that_can_be_completed: bool = False,
+) -> HumanTaskModel:
+ if only_tasks_that_can_be_completed:
+ human_task_query = HumanTaskModel.query.filter_by(
+ process_instance_id=process_instance_id, task_id=task_id, completed=False
+ )
+ else:
+ human_task_query = HumanTaskModel.query.filter_by(
+ process_instance_id=process_instance_id, task_id=task_id
+ )
+
+ human_task: HumanTaskModel = human_task_query.first()
+ if human_task is None:
+ raise (
+ ApiError(
+ error_code="no_human_task",
+ message=(
+ f"Cannot find a task to complete for task id '{task_id}' and"
+ f" process instance {process_instance_id}."
+ ),
+ status_code=500,
+ )
+ )
+ return human_task
diff --git a/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/color_question.json b/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/color_question.json
index 20ea1c12..1ce7072b 100644
--- a/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/color_question.json
+++ b/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/color_question.json
@@ -13,6 +13,18 @@
"selectedColor": {
"$ref": "#/definitions/Color",
"title": "Select color"
+ },
+ "veryImportantFieldButOnlySometimes": {
+ "title": "Very important field",
+ "type": "string"
+ },
+ "building": {
+ "properties": {
+ "floor": {
+ "title": "Floor",
+ "type": "number"
+ }
+ }
}
}
}
diff --git a/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn b/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn
index 7d21851b..d4f1aa5d 100644
--- a/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn
+++ b/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn
@@ -13,7 +13,9 @@
Flow_1my9ag5
Flow_0b04rbg
- awesome_color_options = [{"value": "blue", "label": "Blue"}, {"value": "green", "label": "Green"}]
+ awesome_color_options = [{"value": "blue", "label": "Blue"}, {"value": "green", "label": "Green"}]
+form_ui_hidden_fields = ["veryImportantFieldButOnlySometimes", "building.floor"]
+
diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py
index f52cbc43..c8fd5f6a 100644
--- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py
+++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py
@@ -1687,6 +1687,14 @@ class TestProcessApi(BaseTest):
== "Green"
)
+ # if you set this in task data:
+ # form_ui_hidden_fields = ["veryImportantFieldButOnlySometimes", "building.floor"]
+ # you will get this ui schema:
+ assert response.json["form_ui_schema"] == {
+ "building": {"floor": {"ui:widget": "hidden"}},
+ "veryImportantFieldButOnlySometimes": {"ui:widget": "hidden"},
+ }
+
def test_process_instance_list_with_default_list(
self,
app: Flask,
diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx
index a20e7b7b..48d8e9bb 100644
--- a/spiffworkflow-frontend/src/routes/TaskShow.tsx
+++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx
@@ -200,7 +200,7 @@ export default function TaskShow() {
},
};
} else if (task.form_ui_schema) {
- formUiSchema = JSON.parse(task.form_ui_schema);
+ formUiSchema = task.form_ui_schema;
}
if (task.state !== 'READY') {
formUiSchema = Object.assign(formUiSchema || {}, {