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
66 changes: 66 additions & 0 deletions mdl-examples/bug-tests/360-import-mapping-result-type.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
-- ============================================================================
-- Bug #360: Import-from-mapping result variable type was not registered
-- ============================================================================
--
-- Symptom (before fix):
-- `import from mapping` actions wrote the correct
-- `Forms$ResultHandlingMapping` BSON, but the builder did not register
-- the inferred result type onto the output variable in its variable
-- scope. Subsequent activities that touched the imported result
-- (`change`, `commit`, `$Var/Attribute`, association paths) ran without
-- type info, which caused:
-- - the writer to fall back to untyped expressions
-- - re-describes to lose the right declaration shape
-- - downstream activities to fail with CE0117 in Studio Pro
--
-- After fix:
-- `addImportFromMappingAction` now also writes the result type into
-- `flowBuilder.varTypes`. When the mapping's JSON structure produces a
-- single object, the variable becomes `Module.Entity`. When it produces
-- an array, the variable becomes `List of Module.Entity`. The
-- ResultHandlingMapping BSON path is unchanged.
--
-- Usage:
-- mxcli exec mdl-examples/bug-tests/360-import-mapping-result-type.mdl -p app.mpr
-- mxcli -p app.mpr -c "describe microflow BugTest360.MF_ImportPet"
-- `mx check` against the resulting MPR must report 0 errors and the
-- `change $Pet (...)` statement after the import must resolve.
-- ============================================================================

create module BugTest360;

create json structure BugTest360.JSON_Pet
snippet '{"id": 1, "name": "Fido", "status": "available"}';

create non-persistent entity BugTest360.PetResponse (
PetId : integer,
Name : string,
Status : string
);
/

create import mapping BugTest360.IMM_Pet
with json structure BugTest360.JSON_Pet
{
create BugTest360.PetResponse {
PetId = id,
Name = name,
Status = status
}
};

-- Caller — uses the imported `$Pet` in a downstream `change`. Without
-- the result-type registration, the change activity has no resolved
-- entity for `Status` and Studio Pro surfaces CE0117.
create microflow BugTest360.MF_ImportPet (
$Json: string
)
returns BugTest360.PetResponse as $Pet
begin
$Pet = import from mapping BugTest360.IMM_Pet ($Json);

if $Pet != empty then
change $Pet (Status = $Pet/Status + ' (processed)');
end if;
end;
/
13 changes: 11 additions & 2 deletions mdl/executor/cmd_microflows_builder_calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,8 @@ func (fb *flowBuilder) addImportFromMappingAction(s *ast.ImportFromMappingStmt)
SingleObject: true,
}

// Determine single vs list and result entity from the import mapping
// Determine single vs list and result entity from the import mapping.
resultEntityQN := ""
if fb.backend != nil {
if im, err := fb.backend.GetImportMappingByQualifiedName(s.Mapping.Module, s.Mapping.Name); err == nil {
if im.JsonStructure != "" {
Expand All @@ -1026,7 +1027,8 @@ func (fb *flowBuilder) addImportFromMappingAction(s *ast.ImportFromMappingStmt)
}
}
if len(im.Elements) > 0 && im.Elements[0].Entity != "" {
resultHandling.ResultEntityID = model.ID(im.Elements[0].Entity)
resultEntityQN = im.Elements[0].Entity
resultHandling.ResultEntityID = model.ID(resultEntityQN)
}
}
}
Expand All @@ -1047,6 +1049,13 @@ func (fb *flowBuilder) addImportFromMappingAction(s *ast.ImportFromMappingStmt)

fb.objects = append(fb.objects, activity)
fb.posX += fb.spacing
if s.OutputVariable != "" && resultEntityQN != "" && fb.varTypes != nil {
if resultHandling.SingleObject {
fb.varTypes[s.OutputVariable] = resultEntityQN
} else {
fb.varTypes[s.OutputVariable] = "List of " + resultEntityQN
}
}

if s.ErrorHandling != nil && len(s.ErrorHandling.Body) > 0 {
errorY := fb.posY + VerticalSpacing
Expand Down
72 changes: 72 additions & 0 deletions mdl/executor/cmd_microflows_builder_import_mapping_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0

package executor

import (
"fmt"
"testing"

"github.com/mendixlabs/mxcli/mdl/ast"
"github.com/mendixlabs/mxcli/mdl/backend/mock"
mdltypes "github.com/mendixlabs/mxcli/mdl/types"
"github.com/mendixlabs/mxcli/model"
)

func TestAddImportFromMappingRegistersSingleResultType(t *testing.T) {
fb := importMappingFlowBuilder(t, "Object")

fb.addImportFromMappingAction(&ast.ImportFromMappingStmt{
OutputVariable: "ImportedOrder",
SourceVariable: "Payload",
Mapping: ast.QualifiedName{Module: "Integration", Name: "ImportOrder"},
})

if got := fb.varTypes["ImportedOrder"]; got != "Sales.Order" {
t.Fatalf("ImportedOrder type = %q, want Sales.Order", got)
}
}

func TestAddImportFromMappingRegistersListResultType(t *testing.T) {
fb := importMappingFlowBuilder(t, "Array")

fb.addImportFromMappingAction(&ast.ImportFromMappingStmt{
OutputVariable: "ImportedOrders",
SourceVariable: "Payload",
Mapping: ast.QualifiedName{Module: "Integration", Name: "ImportOrderList"},
})

if got := fb.varTypes["ImportedOrders"]; got != "List of Sales.Order" {
t.Fatalf("ImportedOrders type = %q, want list of Sales.Order", got)
}
}

func importMappingFlowBuilder(t *testing.T, rootElementType string) *flowBuilder {
t.Helper()

return &flowBuilder{
varTypes: map[string]string{},
backend: &mock.MockBackend{
GetImportMappingByQualifiedNameFunc: func(moduleName, name string) (*model.ImportMapping, error) {
if moduleName != "Integration" {
return nil, fmt.Errorf("unexpected module %q", moduleName)
}
return &model.ImportMapping{
JsonStructure: "Integration.OrderPayload",
Elements: []*model.ImportMappingElement{
{Entity: "Sales.Order"},
},
}, nil
},
GetJsonStructureByQualifiedNameFunc: func(moduleName, name string) (*mdltypes.JsonStructure, error) {
if moduleName != "Integration" || name != "OrderPayload" {
return nil, fmt.Errorf("unexpected json structure %s.%s", moduleName, name)
}
return &mdltypes.JsonStructure{
Elements: []*mdltypes.JsonElement{
{ElementType: rootElementType},
},
}, nil
},
},
}
}
Loading