Skip to content

Commit 88d1e50

Browse files
authored
Merge pull request mendixlabs#357 from hjotha/submit/java-action-return-type-inference
fix: infer java action result variable types
2 parents d783877 + a478366 commit 88d1e50

3 files changed

Lines changed: 200 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
-- ============================================================================
2+
-- Bug #352 (part): Java action result variable type inference
3+
-- ============================================================================
4+
--
5+
-- Symptom (before fix):
6+
-- The microflow builder looked up Java action definitions to format
7+
-- entity-typed parameters, but it did NOT register the Java action's
8+
-- return type on the output variable. Subsequent statements that
9+
-- touched the result (`change`, `commit`, `$Result/Attribute`,
10+
-- association paths) had no type info, so:
11+
-- - the writer fell back to untyped expressions
12+
-- - `mx check` could surface CE0117 on dependent activities
13+
-- - describe re-emitted the call without the right variable
14+
-- declaration shape
15+
--
16+
-- After fix:
17+
-- `addCallJavaActionAction` consults the Java action's `ReturnType`
18+
-- and registers the output variable as the appropriate object/list
19+
-- type (`Module.Entity`, `List of Module.Entity`, `System.FileDocument`,
20+
-- etc.) so downstream activities resolve correctly.
21+
--
22+
-- Usage:
23+
-- mxcli exec mdl-examples/bug-tests/352-java-action-return-type-inference.mdl -p app.mpr
24+
-- mxcli -p app.mpr -c "describe microflow BugTest352b.MF_UseJavaResult"
25+
-- `mx check` against the resulting MPR must report 0 errors.
26+
-- ============================================================================
27+
28+
create module BugTest352b;
29+
30+
create persistent entity BugTest352b.LogEntry (
31+
Message : string(500)
32+
);
33+
/
34+
35+
-- Java action returning an entity object — the result variable type must
36+
-- be inferred so downstream `change` / `commit` resolve.
37+
create java action BugTest352b.CreateLogEntry (
38+
Msg: string not null
39+
) returns BugTest352b.LogEntry
40+
as $$
41+
return new com.mendix.systemwideinterfaces.core.IMendixObject();
42+
$$;
43+
/
44+
45+
-- Caller microflow that depends on the inferred return type. The change
46+
-- statement on `$Entry/Message` would fail validation if the Java action
47+
-- result variable type is not registered.
48+
create microflow BugTest352b.MF_UseJavaResult (
49+
$Msg: string
50+
)
51+
returns BugTest352b.LogEntry as $Entry
52+
begin
53+
$Entry = call java action BugTest352b.CreateLogEntry (Msg = $Msg);
54+
change $Entry (Message = $Msg + ' (processed)');
55+
end;
56+
/

mdl/executor/cmd_microflows_builder_calls.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,13 @@ func (fb *flowBuilder) addCallJavaActionAction(s *ast.CallJavaActionStmt) model.
241241
ResultVariableName: s.OutputVariable,
242242
UseReturnVariable: s.OutputVariable != "",
243243
}
244+
if s.OutputVariable != "" && jaDef != nil && fb.varTypes != nil {
245+
if varType := javaActionReturnVarType(jaDef.ReturnType); varType != "" {
246+
fb.varTypes[s.OutputVariable] = varType
247+
} else if inferred := fb.inferGenericJavaActionReturnType(jaDef, s); inferred != "" {
248+
fb.varTypes[s.OutputVariable] = inferred
249+
}
250+
}
244251

245252
activityX := fb.posX
246253
activity := &microflows.ActionActivity{
@@ -268,6 +275,48 @@ func (fb *flowBuilder) addCallJavaActionAction(s *ast.CallJavaActionStmt) model.
268275
return activity.ID
269276
}
270277

278+
func javaActionReturnVarType(returnType javaactions.CodeActionReturnType) string {
279+
switch t := returnType.(type) {
280+
case *javaactions.EntityType:
281+
return t.Entity
282+
case *javaactions.ListType:
283+
if t.Entity != "" {
284+
return "List of " + t.Entity
285+
}
286+
case *javaactions.FileDocumentType:
287+
return "System.FileDocument"
288+
}
289+
return ""
290+
}
291+
292+
func (fb *flowBuilder) inferGenericJavaActionReturnType(jaDef *javaactions.JavaAction, s *ast.CallJavaActionStmt) string {
293+
if jaDef == nil || fb.varTypes == nil || s == nil {
294+
return ""
295+
}
296+
switch t := jaDef.ReturnType.(type) {
297+
case *javaactions.ListType:
298+
if t.Entity != "" {
299+
return ""
300+
}
301+
case javaactions.ListType:
302+
if t.Entity != "" {
303+
return ""
304+
}
305+
default:
306+
return ""
307+
}
308+
for _, arg := range s.Arguments {
309+
valueExpr := strings.TrimPrefix(strings.Trim(fb.exprToString(arg.Value), "'"), "$")
310+
if valueExpr == "" {
311+
continue
312+
}
313+
if typ := fb.varTypes[valueExpr]; strings.HasPrefix(typ, "List of ") {
314+
return typ
315+
}
316+
}
317+
return ""
318+
}
319+
271320
// addCallExternalActionAction creates a CALL EXTERNAL ACTION statement.
272321
func (fb *flowBuilder) addCallExternalActionAction(s *ast.CallExternalActionStmt) model.ID {
273322
serviceQN := s.ServiceName.Module + "." + s.ServiceName.Name
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mendixlabs/mxcli/mdl/ast"
9+
"github.com/mendixlabs/mxcli/mdl/backend/mock"
10+
"github.com/mendixlabs/mxcli/sdk/javaactions"
11+
)
12+
13+
func TestAddJavaAction_FileDocumentReturnRegistersSystemFileDocument(t *testing.T) {
14+
backend := &mock.MockBackend{
15+
ReadJavaActionByNameFunc: func(qualifiedName string) (*javaactions.JavaAction, error) {
16+
if qualifiedName != "Spreadsheet.ExportRows" {
17+
return nil, nil
18+
}
19+
return &javaactions.JavaAction{
20+
ReturnType: &javaactions.FileDocumentType{},
21+
}, nil
22+
},
23+
}
24+
25+
fb := &flowBuilder{
26+
backend: backend,
27+
varTypes: map[string]string{},
28+
declaredVars: map[string]string{},
29+
}
30+
31+
fb.addCallJavaActionAction(&ast.CallJavaActionStmt{
32+
OutputVariable: "GeneratedDocument",
33+
ActionName: ast.QualifiedName{Module: "Spreadsheet", Name: "ExportRows"},
34+
})
35+
36+
if got := fb.varTypes["GeneratedDocument"]; got != "System.FileDocument" {
37+
t.Fatalf("GeneratedDocument type = %q, want System.FileDocument", got)
38+
}
39+
}
40+
41+
func TestAddJavaAction_ConcreteListReturnRegistersListType(t *testing.T) {
42+
backend := &mock.MockBackend{
43+
ReadJavaActionByNameFunc: func(qualifiedName string) (*javaactions.JavaAction, error) {
44+
return &javaactions.JavaAction{
45+
ReturnType: &javaactions.ListType{Entity: "Orders.Order"},
46+
}, nil
47+
},
48+
}
49+
50+
fb := &flowBuilder{
51+
backend: backend,
52+
varTypes: map[string]string{},
53+
declaredVars: map[string]string{},
54+
}
55+
56+
fb.addCallJavaActionAction(&ast.CallJavaActionStmt{
57+
OutputVariable: "FilteredOrders",
58+
ActionName: ast.QualifiedName{Module: "Lists", Name: "FilterOrders"},
59+
})
60+
61+
if got := fb.varTypes["FilteredOrders"]; got != "List of Orders.Order" {
62+
t.Fatalf("FilteredOrders type = %q, want list type", got)
63+
}
64+
}
65+
66+
func TestAddJavaAction_GenericListReturnInheritsInputListType(t *testing.T) {
67+
backend := &mock.MockBackend{
68+
ReadJavaActionByNameFunc: func(qualifiedName string) (*javaactions.JavaAction, error) {
69+
return &javaactions.JavaAction{
70+
ReturnType: &javaactions.ListType{},
71+
}, nil
72+
},
73+
}
74+
75+
fb := &flowBuilder{
76+
varTypes: map[string]string{
77+
"InputOrders": "List of Orders.Order",
78+
},
79+
declaredVars: map[string]string{},
80+
backend: backend,
81+
measurer: &layoutMeasurer{},
82+
}
83+
84+
fb.addCallJavaActionAction(&ast.CallJavaActionStmt{
85+
OutputVariable: "FilteredOrders",
86+
ActionName: ast.QualifiedName{Module: "Lists", Name: "FilterGeneric"},
87+
Arguments: []ast.CallArgument{
88+
{Name: "InputList", Value: &ast.VariableExpr{Name: "InputOrders"}},
89+
},
90+
})
91+
92+
if got := fb.varTypes["FilteredOrders"]; got != "List of Orders.Order" {
93+
t.Fatalf("generic java list result type = %q, want input list type", got)
94+
}
95+
}

0 commit comments

Comments
 (0)