Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions mdl-examples/bug-tests/358-validation-feedback-targets.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
-- ============================================================================
-- Bug #358: Validation feedback object and association targets
-- ============================================================================
--
-- Symptom (before fix):
-- Microflow `validation feedback` statements can target an object
-- itself or an association member, not only a scalar attribute. The
-- MDL grammar accepted only attribute paths, and the formatter
-- emitted only attribute targets. Round-tripping object-only and
-- association targets either failed to re-parse the described MDL or
-- dropped the association name when describing existing Studio Pro
-- models.
--
-- After fix (grammar + formatter only):
-- - Grammar now accepts `validation feedback $Object message '…';`
-- (object-only target).
-- - Grammar treats each path segment of `$Object/Module.Member` as a
-- qualified name so `Module.Association` stays one association
-- segment.
-- - Formatter emits `AssociationName` when `AttributeName` is absent,
-- preserving association targets when describing existing models.
--
-- Scope note:
-- This PR fixes the parser and the formatter (describe path) so
-- existing models with object-only or association targets re-parse
-- and re-describe cleanly. It does NOT extend the builder to make
-- newly authored object-only targets a valid Studio Pro model — the
-- builder still emits `Attribute = ""` which Studio Pro rejects with
-- `[CE0091] No member selected`. Authored validation feedback should
-- continue to use the attribute form until a follow-up extends the
-- builder.
--
-- Usage (verifies the in-scope fix):
-- ./bin/mxcli check mdl-examples/bug-tests/358-validation-feedback-targets.mdl
-- ./bin/mxcli exec mdl-examples/bug-tests/358-validation-feedback-targets.mdl -p app.mpr
-- ./bin/mxcli -p app.mpr -c "describe microflow BugTest358.MF_ValidateAttr"
-- The describe output must keep `validation feedback $Customer/Email
-- message '…';` and `mx check` must report 0 errors.
--
-- The OBJECT-ONLY shape `validation feedback $Customer message '…';`
-- parses and re-describes cleanly (covered by parser/formatter Go tests),
-- but mx check will still surface `[CE0091]` because the builder is
-- out of scope here. Authored MDL should keep using the attribute form.
-- ============================================================================

create module BugTest358;

create entity BugTest358.Customer (
Email : string(200)
);
/

-- Attribute-target validation feedback — must round-trip cleanly under
-- describe → exec → describe and pass `mx check`.
create microflow BugTest358.MF_ValidateAttr (
$Customer: BugTest358.Customer
)
returns boolean as $Valid
begin
declare $Valid boolean = true;
if $Customer/Email = empty then
set $Valid = false;
validation feedback $Customer/Email message 'Email is required';
end if;
return $Valid;
end;
/
2 changes: 2 additions & 0 deletions mdl/executor/cmd_microflows_format_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,8 @@ func formatAction(
if len(parts) >= 3 {
attrPath = varName + "/" + parts[len(parts)-1]
}
} else if a.AssociationName != "" {
attrPath = varName + "/" + a.AssociationName
}
return fmt.Sprintf("validation feedback %s message %s;", attrPath, msgText)

Expand Down
31 changes: 31 additions & 0 deletions mdl/executor/cmd_microflows_format_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,37 @@ func TestFormatAction_ValidationFeedback(t *testing.T) {
}
}

func TestFormatAction_ValidationFeedback_ObjectOnlyTarget(t *testing.T) {
e := newTestExecutor()
action := &microflows.ValidationFeedbackAction{
ObjectVariable: "Customer",
Template: &model.Text{
Translations: map[string]string{"en_US": "Select a customer"},
},
}
got := e.formatAction(action, nil, nil)
want := "validation feedback $Customer message 'Select a customer';"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
}

func TestFormatAction_ValidationFeedback_AssociationTarget(t *testing.T) {
e := newTestExecutor()
action := &microflows.ValidationFeedbackAction{
ObjectVariable: "OrderForm",
AssociationName: "Sales.OrderForm_Customer",
Template: &model.Text{
Translations: map[string]string{"en_US": "Select a customer"},
},
}
got := e.formatAction(action, nil, nil)
want := "validation feedback $OrderForm/Sales.OrderForm_Customer message 'Select a customer';"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
}

func TestFormatAction_LogMessage(t *testing.T) {
e := newTestExecutor()
action := &microflows.LogMessageAction{
Expand Down
4 changes: 2 additions & 2 deletions mdl/grammar/MDLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,7 @@ changeObjectStatement
;

attributePath
: VARIABLE ((SLASH | DOT) (IDENTIFIER | qualifiedName))+
: VARIABLE ((SLASH | DOT) qualifiedName)+
;

// COMMIT $Product; or COMMIT $Product WITH EVENTS; or COMMIT $Product REFRESH;
Expand Down Expand Up @@ -1621,7 +1621,7 @@ throwStatement
// VALIDATION FEEDBACK $Product/Code MESSAGE 'Product code cannot be empty';
// VALIDATION FEEDBACK $Product/Code MESSAGE '{1}' OBJECTS [$Var1, $Var2];
validationFeedbackStatement
: VALIDATION FEEDBACK attributePath MESSAGE expression (OBJECTS LBRACKET expressionList RBRACKET)?
: VALIDATION FEEDBACK (attributePath | VARIABLE) MESSAGE expression (OBJECTS LBRACKET expressionList RBRACKET)?
;

// =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion mdl/grammar/parser/MDLParser.interp

Large diffs are not rendered by default.

Loading
Loading