diff --git a/mdl/executor/cmd_microflows_create.go b/mdl/executor/cmd_microflows_create.go index 96d6de1d..7485407d 100644 --- a/mdl/executor/cmd_microflows_create.go +++ b/mdl/executor/cmd_microflows_create.go @@ -13,6 +13,14 @@ import ( "github.com/mendixlabs/mxcli/sdk/mpr" ) +// isBuiltinModuleEntity returns true for modules whose entities are defined +// internally by the Mendix runtime and are therefore not present in the MPR's +// domain models. These types are serialized using the qualified name reference +// ("System.Workflow", "System.User", etc.) and resolved at runtime. +func isBuiltinModuleEntity(moduleName string) bool { + return moduleName == "System" +} + // execCreateMicroflow handles CREATE MICROFLOW statements. func (e *Executor) execCreateMicroflow(s *ast.CreateMicroflowStmt) error { if e.writer == nil { @@ -105,8 +113,10 @@ func (e *Executor) execCreateMicroflow(s *ast.CreateMicroflowStmt) error { // Validate and add parameters for _, p := range s.Parameters { - // Validate entity references for List and Entity types - if p.Type.EntityRef != nil { + // Validate entity references for List and Entity types. + // Built-in modules (e.g. System) are not stored in the MPR domain models; + // their types are serialized by qualified name and resolved at runtime. + if p.Type.EntityRef != nil && !isBuiltinModuleEntity(p.Type.EntityRef.Module) { entityID := entityResolver(*p.Type.EntityRef) if entityID == "" { return fmt.Errorf("entity '%s.%s' not found for parameter '%s'", @@ -133,8 +143,10 @@ func (e *Executor) execCreateMicroflow(s *ast.CreateMicroflowStmt) error { // Validate and set return type if s.ReturnType != nil { - // Validate entity references for return type - if s.ReturnType.Type.EntityRef != nil { + // Validate entity references for return type. + // Built-in modules (e.g. System) are not stored in the MPR domain models; + // their types are serialized by qualified name and resolved at runtime. + if s.ReturnType.Type.EntityRef != nil && !isBuiltinModuleEntity(s.ReturnType.Type.EntityRef.Module) { entityID := entityResolver(*s.ReturnType.Type.EntityRef) if entityID == "" { return fmt.Errorf("entity '%s.%s' not found for return type", diff --git a/mdl/executor/cmd_microflows_format_action.go b/mdl/executor/cmd_microflows_format_action.go index 626120b7..d03c0e08 100644 --- a/mdl/executor/cmd_microflows_format_action.go +++ b/mdl/executor/cmd_microflows_format_action.go @@ -23,8 +23,8 @@ func (e *Executor) formatActivity( case *microflows.EndEvent: if activity.ReturnValue != "" { - returnVal := activity.ReturnValue - if !strings.HasPrefix(returnVal, "$") { + returnVal := strings.TrimSuffix(activity.ReturnValue, "\n") + if !strings.HasPrefix(returnVal, "$") && !isMendixKeyword(returnVal) { returnVal = "$" + returnVal } return fmt.Sprintf("RETURN %s;", returnVal) @@ -846,3 +846,13 @@ func (e *Executor) formatExecuteDatabaseQueryAction(a *microflows.ExecuteDatabas sb.WriteString(";") return sb.String() } + +// isMendixKeyword returns true for Mendix expression keywords that must not be +// prefixed with "$" when serialized as a RETURN value. +func isMendixKeyword(s string) bool { + switch s { + case "empty", "true", "false", "null": + return true + } + return false +} diff --git a/mdl/executor/roundtrip_microflow_test.go b/mdl/executor/roundtrip_microflow_test.go index 798559d3..2af290ed 100644 --- a/mdl/executor/roundtrip_microflow_test.go +++ b/mdl/executor/roundtrip_microflow_test.go @@ -583,6 +583,32 @@ END;` ) } +// TestRoundtripMicroflow_SystemEntityParameter tests that microflows with +// System.* built-in entity parameter types can be created (issue #29). +// System entities (System.Workflow, System.User, etc.) are not stored in the +// MPR domain models — they are serialized by qualified name at runtime. +func TestRoundtripMicroflow_SystemEntityParameter(t *testing.T) { + env := setupTestEnv(t) + defer env.teardown() + + mfName := testModule + ".RT_SystemEntityParam" + env.registerCleanup("microflow", mfName) + + createMDL := `CREATE MICROFLOW ` + mfName + ` ( + $Workflow: System.Workflow, + $Count: Integer +) RETURNS List of System.User +BEGIN + RETURN empty; +END;` + + assertMicroflowContains(t, env, mfName, createMDL, + // "empty" is a reserved keyword — must round-trip as "RETURN empty", not "$empty" + []string{"System.Workflow", "System.User", "RETURN empty"}, + []string{"RETURN $empty"}, + ) +} + // TestRoundtripMicroflow_Position tests @position is emitted in DESCRIBE output. func TestRoundtripMicroflow_Position(t *testing.T) { env := setupTestEnv(t) diff --git a/sdk/mpr/writer_microflow.go b/sdk/mpr/writer_microflow.go index ba8a0c3d..f292b153 100644 --- a/sdk/mpr/writer_microflow.go +++ b/sdk/mpr/writer_microflow.go @@ -136,11 +136,9 @@ func (w *Writer) serializeMicroflow(mf *microflows.Microflow) ([]byte, error) { } // Add remaining optional fields - returnVarName := mf.ReturnVariableName - if returnVarName == "" { - returnVarName = "Variable" // Default value used by Mendix - } - doc = append(doc, bson.E{Key: "ReturnVariableName", Value: returnVarName}) + // ReturnVariableName is "" by default (Studio Pro convention). + // Only set a custom name when explicitly specified via "RETURNS xxx AS $VarName". + doc = append(doc, bson.E{Key: "ReturnVariableName", Value: mf.ReturnVariableName}) doc = append(doc, bson.E{Key: "StableId", Value: idToBsonBinary(generateUUID())}) doc = append(doc, bson.E{Key: "Url", Value: ""}) doc = append(doc, bson.E{Key: "UrlSearchParameters", Value: bson.A{int32(1)}}) @@ -223,7 +221,7 @@ func serializeMicroflowParameter(p *microflows.MicroflowParameter, posX int) bso {Key: "$Type", Value: "Microflows$MicroflowParameter"}, {Key: "DefaultValue", Value: ""}, {Key: "Documentation", Value: p.Documentation}, - {Key: "HasVariableNameBeenChanged", Value: true}, + {Key: "HasVariableNameBeenChanged", Value: false}, {Key: "IsRequired", Value: true}, {Key: "Name", Value: p.Name}, {Key: "RelativeMiddlePoint", Value: relativeMiddlePoint},