Skip to content

Commit d996121

Browse files
authored
Merge pull request #338 from hjotha/submit/writer-showpage-parameter-mapping-validity
fix: write valid show-page parameter mappings
2 parents 89a60f6 + fef970c commit d996121

3 files changed

Lines changed: 125 additions & 3 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
-- ============================================================================
2+
-- Bug #295: SHOW PAGE parameter mappings produced unopenable Studio Pro MPRs
3+
-- ============================================================================
4+
--
5+
-- Symptom (before fix):
6+
-- When `mxcli exec` wrote a microflow `show page X (Param: $Var)`, the
7+
-- `Forms$PageParameterMapping.Variable` field was serialized as BSON
8+
-- `null`. Studio Pro rejected this on project load with a runtime
9+
-- exception, making the entire MPR unopenable. The originating bug
10+
-- also affected SNIPPET ACTIONBUTTON SHOW_PAGE (#295), but the root
11+
-- cause is in the writer's serialization of PageParameterMapping.
12+
--
13+
-- Additionally, `FormSettings.ParameterMappings` used the mapping count
14+
-- as the BSON storage-list first element instead of Mendix's storage-list
15+
-- marker (`int32(2)`).
16+
--
17+
-- After fix:
18+
-- `writer_microflow_actions.go` now writes
19+
-- - `Variable` as a valid empty `Forms$PageVariable` object via
20+
-- `emptyPageVariable()`
21+
-- - `ParameterMappings` storage-list with the constant marker `int32(2)`
22+
--
23+
-- Usage:
24+
-- mxcli exec mdl-examples/bug-tests/295-showpage-parameter-mapping-validity.mdl -p app.mpr
25+
-- mxcli -p app.mpr -c "describe microflow BugTest295.ACT_OpenProductPage"
26+
-- `mx check` against the resulting MPR must report 0 errors and the
27+
-- project must be openable in Studio Pro.
28+
-- ============================================================================
29+
30+
create module BugTest295;
31+
32+
create entity BugTest295.Product (
33+
Name : string(100)
34+
);
35+
/
36+
37+
create page BugTest295.Product_Detail
38+
(
39+
params: {
40+
$Product: BugTest295.Product
41+
},
42+
title: 'Product Detail',
43+
layout: Atlas_Core.Atlas_Default
44+
)
45+
{
46+
dynamictext txtName (content: 'Product detail', rendermode: H4)
47+
}
48+
/
49+
50+
create microflow BugTest295.ACT_OpenProductPage (
51+
$Product: BugTest295.Product
52+
)
53+
begin
54+
show page BugTest295.Product_Detail (Product: $Product);
55+
end;
56+
/

sdk/mpr/showpage_roundtrip_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/mendixlabs/mxcli/model"
99
"github.com/mendixlabs/mxcli/sdk/microflows"
1010
"go.mongodb.org/mongo-driver/bson"
11+
"go.mongodb.org/mongo-driver/bson/primitive"
1112
)
1213

1314
// TestShowPageAction_Roundtrip verifies that a ShowPageAction with parameters
@@ -62,6 +63,59 @@ func TestShowPageAction_Roundtrip(t *testing.T) {
6263
}
6364
}
6465

66+
func TestShowPageAction_WritesValidPageParameterMapping(t *testing.T) {
67+
action := &microflows.ShowPageAction{
68+
BaseElement: model.BaseElement{ID: "test-action-id"},
69+
PageName: "Sales.Product_NewEdit",
70+
PageParameterMappings: []*microflows.PageParameterMapping{
71+
{
72+
BaseElement: model.BaseElement{ID: "test-mapping-id"},
73+
Parameter: "Sales.Product_NewEdit.Product",
74+
Argument: "$Product",
75+
},
76+
},
77+
}
78+
79+
doc := serializeMicroflowAction(action)
80+
data, err := bson.Marshal(doc)
81+
if err != nil {
82+
t.Fatalf("failed to marshal BSON: %v", err)
83+
}
84+
85+
var raw map[string]any
86+
if err := bson.Unmarshal(data, &raw); err != nil {
87+
t.Fatalf("failed to unmarshal BSON: %v", err)
88+
}
89+
90+
formSettings := toMap(raw["FormSettings"])
91+
if formSettings == nil {
92+
t.Fatal("FormSettings missing")
93+
}
94+
95+
mappings, ok := formSettings["ParameterMappings"].(primitive.A)
96+
if !ok {
97+
t.Fatalf("ParameterMappings type = %T, want primitive.A", formSettings["ParameterMappings"])
98+
}
99+
if len(mappings) != 2 {
100+
t.Fatalf("ParameterMappings length = %d, want marker plus one mapping", len(mappings))
101+
}
102+
if marker, ok := mappings[0].(int32); !ok || marker != 2 {
103+
t.Fatalf("ParameterMappings marker = %#v, want int32(2)", mappings[0])
104+
}
105+
106+
mapping := toMap(mappings[1])
107+
if mapping == nil {
108+
t.Fatal("PageParameterMapping missing")
109+
}
110+
variable := toMap(mapping["Variable"])
111+
if variable == nil {
112+
t.Fatal("Variable is nil; Studio Pro rejects null page parameter mapping variables")
113+
}
114+
if got := extractString(variable["$Type"]); got != "Forms$PageVariable" {
115+
t.Fatalf("Variable $Type = %q, want Forms$PageVariable", got)
116+
}
117+
}
118+
65119
// TestShowPageAction_RoundtripNoParams verifies that a ShowPageAction without parameters
66120
// survives BSON serialization/deserialization.
67121
func TestShowPageAction_RoundtripNoParams(t *testing.T) {

sdk/mpr/writer_microflow_actions.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,15 +342,16 @@ func serializeMicroflowAction(action microflows.MicroflowAction) bson.D {
342342
formSettingsID = model.ID(generateUUID())
343343
}
344344

345-
// Build ParameterMappings inside FormSettings
346-
paramMappings := bson.A{int32(len(a.PageParameterMappings))}
345+
// Build ParameterMappings inside FormSettings. Mendix storage lists use
346+
// a marker as the first element; it is not the number of mappings.
347+
paramMappings := bson.A{int32(2)}
347348
for _, pm := range a.PageParameterMappings {
348349
mapping := bson.D{
349350
{Key: "$ID", Value: idToBsonBinary(string(pm.ID))},
350351
{Key: "$Type", Value: "Forms$PageParameterMapping"}, // Forms$, not Microflows$
351352
{Key: "Argument", Value: pm.Argument},
352353
{Key: "Parameter", Value: pm.Parameter}, // BY_NAME_REFERENCE
353-
{Key: "Variable", Value: nil},
354+
{Key: "Variable", Value: emptyPageVariable()},
354355
}
355356
paramMappings = append(paramMappings, mapping)
356357
}
@@ -475,6 +476,17 @@ func serializeMicroflowAction(action microflows.MicroflowAction) bson.D {
475476
}
476477
}
477478

479+
func emptyPageVariable() bson.D {
480+
return bson.D{
481+
{Key: "$ID", Value: idToBsonBinary(generateUUID())},
482+
{Key: "$Type", Value: "Forms$PageVariable"},
483+
{Key: "PageParameter", Value: ""},
484+
{Key: "SnippetParameter", Value: ""},
485+
{Key: "UseAllPages", Value: false},
486+
{Key: "Widget", Value: ""},
487+
}
488+
}
489+
478490
// serializeRestCallAction serializes a RestCallAction to BSON.
479491
// Storage name is "Microflows$RestCallAction" (same as qualified name).
480492
func serializeRestCallAction(a *microflows.RestCallAction) bson.D {

0 commit comments

Comments
 (0)