Skip to content

Commit f5f893c

Browse files
authored
Merge pull request mendixlabs#360 from hjotha/submit/import-mapping-result-variable-type
fix: register import mapping result variable types
2 parents 88d1e50 + e5d6418 commit f5f893c

3 files changed

Lines changed: 149 additions & 2 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
-- ============================================================================
2+
-- Bug #360: Import-from-mapping result variable type was not registered
3+
-- ============================================================================
4+
--
5+
-- Symptom (before fix):
6+
-- `import from mapping` actions wrote the correct
7+
-- `Forms$ResultHandlingMapping` BSON, but the builder did not register
8+
-- the inferred result type onto the output variable in its variable
9+
-- scope. Subsequent activities that touched the imported result
10+
-- (`change`, `commit`, `$Var/Attribute`, association paths) ran without
11+
-- type info, which caused:
12+
-- - the writer to fall back to untyped expressions
13+
-- - re-describes to lose the right declaration shape
14+
-- - downstream activities to fail with CE0117 in Studio Pro
15+
--
16+
-- After fix:
17+
-- `addImportFromMappingAction` now also writes the result type into
18+
-- `flowBuilder.varTypes`. When the mapping's JSON structure produces a
19+
-- single object, the variable becomes `Module.Entity`. When it produces
20+
-- an array, the variable becomes `List of Module.Entity`. The
21+
-- ResultHandlingMapping BSON path is unchanged.
22+
--
23+
-- Usage:
24+
-- mxcli exec mdl-examples/bug-tests/360-import-mapping-result-type.mdl -p app.mpr
25+
-- mxcli -p app.mpr -c "describe microflow BugTest360.MF_ImportPet"
26+
-- `mx check` against the resulting MPR must report 0 errors and the
27+
-- `change $Pet (...)` statement after the import must resolve.
28+
-- ============================================================================
29+
30+
create module BugTest360;
31+
32+
create json structure BugTest360.JSON_Pet
33+
snippet '{"id": 1, "name": "Fido", "status": "available"}';
34+
35+
create non-persistent entity BugTest360.PetResponse (
36+
PetId : integer,
37+
Name : string,
38+
Status : string
39+
);
40+
/
41+
42+
create import mapping BugTest360.IMM_Pet
43+
with json structure BugTest360.JSON_Pet
44+
{
45+
create BugTest360.PetResponse {
46+
PetId = id,
47+
Name = name,
48+
Status = status
49+
}
50+
};
51+
52+
-- Caller — uses the imported `$Pet` in a downstream `change`. Without
53+
-- the result-type registration, the change activity has no resolved
54+
-- entity for `Status` and Studio Pro surfaces CE0117.
55+
create microflow BugTest360.MF_ImportPet (
56+
$Json: string
57+
)
58+
returns BugTest360.PetResponse as $Pet
59+
begin
60+
$Pet = import from mapping BugTest360.IMM_Pet ($Json);
61+
62+
if $Pet != empty then
63+
change $Pet (Status = $Pet/Status + ' (processed)');
64+
end if;
65+
end;
66+
/

mdl/executor/cmd_microflows_builder_calls.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,8 @@ func (fb *flowBuilder) addImportFromMappingAction(s *ast.ImportFromMappingStmt)
10941094
SingleObject: true,
10951095
}
10961096

1097-
// Determine single vs list and result entity from the import mapping
1097+
// Determine single vs list and result entity from the import mapping.
1098+
resultEntityQN := ""
10981099
if fb.backend != nil {
10991100
if im, err := fb.backend.GetImportMappingByQualifiedName(s.Mapping.Module, s.Mapping.Name); err == nil {
11001101
if im.JsonStructure != "" {
@@ -1108,7 +1109,8 @@ func (fb *flowBuilder) addImportFromMappingAction(s *ast.ImportFromMappingStmt)
11081109
}
11091110
}
11101111
if len(im.Elements) > 0 && im.Elements[0].Entity != "" {
1111-
resultHandling.ResultEntityID = model.ID(im.Elements[0].Entity)
1112+
resultEntityQN = im.Elements[0].Entity
1113+
resultHandling.ResultEntityID = model.ID(resultEntityQN)
11121114
}
11131115
}
11141116
}
@@ -1129,6 +1131,13 @@ func (fb *flowBuilder) addImportFromMappingAction(s *ast.ImportFromMappingStmt)
11291131

11301132
fb.objects = append(fb.objects, activity)
11311133
fb.posX += fb.spacing
1134+
if s.OutputVariable != "" && resultEntityQN != "" && fb.varTypes != nil {
1135+
if resultHandling.SingleObject {
1136+
fb.varTypes[s.OutputVariable] = resultEntityQN
1137+
} else {
1138+
fb.varTypes[s.OutputVariable] = "List of " + resultEntityQN
1139+
}
1140+
}
11321141

11331142
if s.ErrorHandling != nil && len(s.ErrorHandling.Body) > 0 {
11341143
errorY := fb.posY + VerticalSpacing
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"fmt"
7+
"testing"
8+
9+
"github.com/mendixlabs/mxcli/mdl/ast"
10+
"github.com/mendixlabs/mxcli/mdl/backend/mock"
11+
mdltypes "github.com/mendixlabs/mxcli/mdl/types"
12+
"github.com/mendixlabs/mxcli/model"
13+
)
14+
15+
func TestAddImportFromMappingRegistersSingleResultType(t *testing.T) {
16+
fb := importMappingFlowBuilder(t, "Object")
17+
18+
fb.addImportFromMappingAction(&ast.ImportFromMappingStmt{
19+
OutputVariable: "ImportedOrder",
20+
SourceVariable: "Payload",
21+
Mapping: ast.QualifiedName{Module: "Integration", Name: "ImportOrder"},
22+
})
23+
24+
if got := fb.varTypes["ImportedOrder"]; got != "Sales.Order" {
25+
t.Fatalf("ImportedOrder type = %q, want Sales.Order", got)
26+
}
27+
}
28+
29+
func TestAddImportFromMappingRegistersListResultType(t *testing.T) {
30+
fb := importMappingFlowBuilder(t, "Array")
31+
32+
fb.addImportFromMappingAction(&ast.ImportFromMappingStmt{
33+
OutputVariable: "ImportedOrders",
34+
SourceVariable: "Payload",
35+
Mapping: ast.QualifiedName{Module: "Integration", Name: "ImportOrderList"},
36+
})
37+
38+
if got := fb.varTypes["ImportedOrders"]; got != "List of Sales.Order" {
39+
t.Fatalf("ImportedOrders type = %q, want list of Sales.Order", got)
40+
}
41+
}
42+
43+
func importMappingFlowBuilder(t *testing.T, rootElementType string) *flowBuilder {
44+
t.Helper()
45+
46+
return &flowBuilder{
47+
varTypes: map[string]string{},
48+
backend: &mock.MockBackend{
49+
GetImportMappingByQualifiedNameFunc: func(moduleName, name string) (*model.ImportMapping, error) {
50+
if moduleName != "Integration" {
51+
return nil, fmt.Errorf("unexpected module %q", moduleName)
52+
}
53+
return &model.ImportMapping{
54+
JsonStructure: "Integration.OrderPayload",
55+
Elements: []*model.ImportMappingElement{
56+
{Entity: "Sales.Order"},
57+
},
58+
}, nil
59+
},
60+
GetJsonStructureByQualifiedNameFunc: func(moduleName, name string) (*mdltypes.JsonStructure, error) {
61+
if moduleName != "Integration" || name != "OrderPayload" {
62+
return nil, fmt.Errorf("unexpected json structure %s.%s", moduleName, name)
63+
}
64+
return &mdltypes.JsonStructure{
65+
Elements: []*mdltypes.JsonElement{
66+
{ElementType: rootElementType},
67+
},
68+
}, nil
69+
},
70+
},
71+
}
72+
}

0 commit comments

Comments
 (0)