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
39 changes: 35 additions & 4 deletions mdl/executor/cmd_microflows_builder_calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,32 @@ func (fb *flowBuilder) addCallJavaActionAction(s *ast.CallJavaActionStmt) model.
}
}

// Build a map of parameter name -> param type for the Java action
// Build a map of parameter name -> param type for the Java action.
// resolvedBasicParams tracks parameters whose type was successfully
// resolved via the Java action definition AND is not an entity-type or
// microflow-type parameter (i.e. anything that lands in
// BasicCodeActionParameterValue: String, Integer, Boolean, ListType,
// ParameterizedEntityType, etc.). When the MDL author binds such a
// parameter to `empty`, Studio Pro authors `Argument: "empty"` (the MDL
// literal string) rather than `Argument: ""`, the unbound marker. The
// distinction matters: `mx check` reports CE0126 "Missing value for
// parameter X" when a typed parameter receives the unbound `""` shape.
//
// Without a backend lookup (jaDef == nil) we fall back to the prior
// `""` behaviour to preserve the documented "intentionally unbound"
// semantics of PROPOSAL_microflow_empty_java_action_argument.md.
entityTypeParams := make(map[string]bool)
microflowTypeParams := make(map[string]bool)
resolvedBasicParams := make(map[string]bool)
if jaDef != nil {
for _, p := range jaDef.Parameters {
if _, ok := p.ParameterType.(*javaactions.EntityTypeParameterType); ok {
switch p.ParameterType.(type) {
case *javaactions.EntityTypeParameterType:
entityTypeParams[p.Name] = true
} else if _, ok := p.ParameterType.(*javaactions.MicroflowType); ok {
case *javaactions.MicroflowType:
microflowTypeParams[p.Name] = true
default:
resolvedBasicParams[p.Name] = true
}
}
}
Expand Down Expand Up @@ -285,9 +302,23 @@ func (fb *flowBuilder) addCallJavaActionAction(s *ast.CallJavaActionStmt) model.
Microflow: "",
}
} else {
// When the Java action definition is available and the
// parameter is a typed BasicParameterType (anything that
// isn't entity-type or microflow-type — String, Integer,
// Boolean, ListType, ParameterizedEntityType, etc.), Studio
// Pro authors `Argument: "empty"` for the MDL `empty`
// literal. Without that information (jaDef == nil) keep the
// blank-string "intentionally unbound" marker that
// PROPOSAL_microflow_empty_java_action_argument.md
// established for code-action callers without backend
// resolution.
argument := ""
if resolvedBasicParams[arg.Name] {
argument = "empty"
}
value = &microflows.BasicCodeActionParameterValue{
BaseElement: model.BaseElement{ID: model.ID(types.GenerateID())},
Argument: "",
Argument: argument,
}
}
} else {
Expand Down
67 changes: 67 additions & 0 deletions mdl/executor/cmd_microflows_builder_java_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,73 @@ func TestBuildJavaAction_EmptyArgumentPreservesEmptyBasicValue(t *testing.T) {
}
}

// TestBuildJavaAction_EmptyResolvedBasicArgumentEmitsEmptyKeyword pins
// the BSON shape Studio Pro authors when a typed (non-entity-type,
// non-microflow-type) Java action parameter is bound to MDL `empty`:
// the BasicCodeActionParameterValue.Argument holds the literal string
// "empty". Emitting the blank `""` for such a parameter triggers
// `mx check` CE0126 "Missing value for parameter X" because the model
// treats the parameter as missing rather than explicitly empty. The
// behaviour applies regardless of the inner type (String, ListType,
// ParameterizedEntityType, …) — the discriminator is whether the
// backend resolved the parameter at all.
func TestBuildJavaAction_EmptyResolvedBasicArgumentEmitsEmptyKeyword(t *testing.T) {
cases := []struct {
name string
paramType javaactions.CodeActionParameterType
}{
{"list", &javaactions.ListType{Entity: "SampleModule.Tag"}},
{"string", &javaactions.StringType{}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
fb := &flowBuilder{
posX: 100,
posY: 100,
spacing: HorizontalSpacing,
backend: &mock.MockBackend{
ReadJavaActionByNameFunc: func(qualifiedName string) (*javaactions.JavaAction, error) {
if qualifiedName != "SampleModule.AddBatch" {
t.Fatalf("java action lookup = %q", qualifiedName)
}
return &javaactions.JavaAction{
Parameters: []*javaactions.JavaActionParameter{
{Name: "Param", ParameterType: tc.paramType},
},
}, nil
},
},
}
stmt := &ast.CallJavaActionStmt{
ActionName: ast.QualifiedName{Module: "SampleModule", Name: "AddBatch"},
Arguments: []ast.CallArgument{
{Name: "Param", Value: &ast.LiteralExpr{Kind: ast.LiteralEmpty}},
},
}

id := fb.addCallJavaActionAction(stmt)
var activity *microflows.ActionActivity
for _, obj := range fb.objects {
if obj.GetID() == id {
activity, _ = obj.(*microflows.ActionActivity)
break
}
}
if activity == nil {
t.Fatal("expected Java action activity")
}
action := activity.Action.(*microflows.JavaActionCallAction)
value, ok := action.ParameterMappings[0].Value.(*microflows.BasicCodeActionParameterValue)
if !ok {
t.Fatalf("mapping value = %T, want *BasicCodeActionParameterValue", action.ParameterMappings[0].Value)
}
if value.Argument != "empty" {
t.Fatalf("resolved empty argument = %q, want %q", value.Argument, "empty")
}
})
}
}

func TestBuildJavaAction_EmptyMicroflowArgumentUsesMicroflowParameterValue(t *testing.T) {
fb := &flowBuilder{
posX: 100,
Expand Down
Loading