Workflows: Take missing-value warnings from the displayed job

Describe only sees literal input values, so object references such as cases
were always reported as missing, and the warning did not update when a value
was edited. Compute these warnings from the job's bindings and refresh them
when a binding changes.
This commit is contained in:
Kristian Bendiksen committed 2026-10-21 11:19:35 +02:00
1 parent 3650b4fbfd
commit a47a871fea
7 files changed
+118 -1

No files matched your search

@@ -91,7 +91,7 @@ RimWorkflowValidationTools::Result RimWorkflowValidationTools::issuesFromDescrib
result.issues.push_back( { .task = task.value( "name" ).toString(),
.field = field.toString(),
.severity = RimWorkflowIssue::Severity::Warning,
.message = QString( "Input '%1' has no value" ).arg( field.toString() ) } );
.message = missingValueMessage( field.toString() ) } );
}
}
return result;
@@ -110,3 +110,40 @@ QString RimWorkflowValidationTools::taskFromMessage( const QString& message, con
}
return found;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWorkflowValidationTools::missingValueMessage( const QString& fieldName )
{
return QString( "Input '%1' has no value" ).arg( fieldName );
}
//--------------------------------------------------------------------------------------------------
/// The saved definition only carries literal values, so describe reports object references (cases,
/// well paths, views) as missing, and the result goes stale when the user edits a value. The job's
/// own bindings are the source of truth for the fields they cover.
//--------------------------------------------------------------------------------------------------
QJsonArray RimWorkflowValidationTools::issuesWithJobValues( const QString& taskName,
const QJsonArray& taskIssues,
const std::map<QString, bool>& fieldHasValue )
{
QJsonArray result;
for ( const QJsonValue& value : taskIssues )
{
const QJsonObject issue = value.toObject();
const QString field = issue.value( "field" ).toString();
const bool isMissingValueWarning = issue.value( "severity" ).toString() == "warning" &&
issue.value( "message" ).toString() == missingValueMessage( field );
if ( isMissingValueWarning && fieldHasValue.contains( field ) ) continue;
result.append( issue );
}
for ( const auto& [field, hasValue] : fieldHasValue )
{
if ( hasValue ) continue;
result.append(
QJsonObject{ { "task", taskName }, { "field", field }, { "severity", "warning" }, { "message", missingValueMessage( field ) } } );
}
return result;
}
@@ -20,9 +20,11 @@
#include "RimWorkflowDefinition.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QStringList>
#include <map>
#include <vector>
//==================================================================================================
@@ -40,4 +42,9 @@ struct Result
Result issuesFromHelperResult( const QJsonObject& helperResult, const QStringList& nodeNames );
Result issuesFromDescribe( const QJsonObject& describe, const QJsonObject& describeError, const QStringList& nodeNames );
QString taskFromMessage( const QString& message, const QStringList& nodeNames );
QString missingValueMessage( const QString& fieldName );
// Replace the missing-value warnings of a task with warnings derived from the values of a job.
// `fieldHasValue` maps the task's bound input fields to whether the job gives them a value.
QJsonArray issuesWithJobValues( const QString& taskName, const QJsonArray& taskIssues, const std::map<QString, bool>& fieldHasValue );
} // namespace RimWorkflowValidationTools
@@ -241,3 +241,30 @@ TEST( RimWorkflowValidationTools, issuesFromHelperResult )
EXPECT_EQ( RimWorkflowIssue::Severity::Warning, result.issues[0].severity );
EXPECT_EQ( "paths", result.issues[0].field );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RimWorkflowValidationTools, issuesWithJobValues )
{
const QJsonObject missingCase{ { "task", "select" },
{ "field", "case" },
{ "severity", "warning" },
{ "message", RimWorkflowValidationTools::missingValueMessage( "case" ) } };
const QJsonObject otherIssue{ { "task", "select" }, { "field", "" }, { "severity", "error" }, { "message", "Broken" } };
// A value in the job removes the stale warning from describe
QJsonArray issues = RimWorkflowValidationTools::issuesWithJobValues( "select", { missingCase, otherIssue }, { { "case", true } } );
ASSERT_EQ( 1, issues.size() );
EXPECT_EQ( "Broken", issues[0].toObject().value( "message" ).toString() );
// A missing value in the job gives one warning, even if describe also reported it
issues = RimWorkflowValidationTools::issuesWithJobValues( "select", { missingCase }, { { "case", false } } );
ASSERT_EQ( 1, issues.size() );
EXPECT_EQ( "case", issues[0].toObject().value( "field" ).toString() );
EXPECT_EQ( "warning", issues[0].toObject().value( "severity" ).toString() );
// Fields the job does not bind keep the warnings from describe
issues = RimWorkflowValidationTools::issuesWithJobValues( "select", { missingCase }, {} );
EXPECT_EQ( 1, issues.size() );
}
@@ -1621,6 +1621,7 @@ void RiuMainWindow::workflowBindingChanged( const RimWorkflowFieldBinding* bindi
{
m_workflowEditor->graphView()->setTaskInputValue( task->taskName(), binding->fieldName(), binding->displayValue() );
}
m_workflowEditor->updateTaskIssues();
}
//--------------------------------------------------------------------------------------------------
@@ -31,6 +31,7 @@
#include "Workflow/RimWorkflowPortCompatibility.h"
#include "Workflow/RimWorkflowTaskCatalog.h"
#include "Workflow/RimWorkflowTaskInput.h"
#include "Workflow/RimWorkflowValidationTools.h"
#include "cafCmdFeature.h"
#include "cafCmdFeatureManager.h"
@@ -260,6 +261,43 @@ void RiuWorkflowEditorWidget::updateTaskInputValues()
if ( binding ) m_graphView->setTaskInputValue( task->taskName(), binding->fieldName(), binding->displayValue() );
}
}
updateTaskIssues();
}
//--------------------------------------------------------------------------------------------------
/// Show the issues of the workflow, with missing-value warnings taken from the displayed job
//--------------------------------------------------------------------------------------------------
void RiuWorkflowEditorWidget::updateTaskIssues()
{
if ( !m_workflow || !m_job ) return;
for ( const QJsonValue& value : m_workflow->graph().value( "tasks" ).toArray() )
{
const QJsonObject graphTask = value.toObject();
const QString taskName = graphTask.value( "name" ).toString();
RimWorkflowTaskInput* taskInput = m_job->taskInput( taskName );
std::map<QString, bool> fieldHasValue;
for ( const QJsonValue& field : graphTask.value( "config_fields" ).toArray() )
{
const QJsonObject schema = field.toObject();
const QString fieldName = schema.value( "name" ).toString();
bool hasValue = false;
if ( taskInput )
{
for ( const RimWorkflowFieldBinding* binding : taskInput->items() )
{
if ( binding && binding->fieldName() == fieldName ) hasValue = !binding->toJsonValue().isNull();
}
}
fieldHasValue[fieldName] = hasValue || !schema.value( "required" ).toBool();
}
m_graphView->setTaskIssues( taskName,
RimWorkflowValidationTools::issuesWithJobValues( taskName,
graphTask.value( "issues" ).toArray(),
fieldHasValue ) );
}
}
//--------------------------------------------------------------------------------------------------
@@ -57,6 +57,7 @@ public:
void refresh();
void updateJobState();
void updateTaskInputValues();
void updateTaskIssues();
RiuWorkflowGraphView* graphView() const;
RiuWorkflowTaskPalette* palette() const;
+6
View File
@@ -192,6 +192,12 @@ Two layers of validation run:
dropped, and a 30 s watchdog stops hung processes.
`RimWorkflowValidationTools::issuesFromHelperResult` maps the errors to
tasks; anything left over goes to the status line.
- **Missing values** come from the displayed job, not from describe. The saved
definition only carries literal values, so describe would report cases, well
paths and views as missing. The editor replaces describe's "has no value"
warnings with ones computed from the job's bindings
(`RimWorkflowValidationTools::issuesWithJobValues`). They update as soon as
a value is edited.
Running a workflow whose last validation failed asks for confirmation first.