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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-25", "symptom": "mendixlabs/mxcli#1652: `check -p --references` passed a microflow in an ordinary module that did `retrieve $l from AddOn.Internal` or `$i = create AddOn.Internal` on a Hidden entity of a Protected (add-on) module; mx check refused it with CE1406 (Retrieve object(s) activity / Create object activity … is not allowed to use entity)", "cause": "checkFlow checked an entity only in a flow's parameter and return types, and checkFlowMembers checked the members a CreateObjectStmt assigns and a RetrieveStmt's XPath names, but not s.EntityType / s.Source. A bare create or a retrieve without where reached no check", "file": "mdl/executor/validate_protected_module_members.go (checkFlowMembers)", "insight": "CE1406 is raised per element a flow names, not per member: every place a flow body names an entity (create, database retrieve) needs the same entityRef call the signature gets. entityRef already folds in the script-stated, stored and created-by-script levels, so reuse it rather than a new lookup. An association retrieve names an association, not an entity, and stays with the member check.", "refs": ["mendixlabs/mxcli#1652", "mendixlabs/mxcli#1498"], "ce": ["CE1406"], "rules": []}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Fixed

- **`check -p --references` reports CE1406 for a database retrieve or a create of a Hidden entity of a Protected module** (mendixlabs/mxcli#1652) — `retrieve $l from AddOn.Internal` and `$i = create AddOn.Internal` in an ordinary module passed `check` and `exec`, and mxbuild refused the build with CE1406 ("Retrieve object(s) activity … is not allowed to use entity", "Create object activity … is not allowed to use entity"). The flow-body check looked at the members a create or retrieve names, never at the entity itself, so a bare create or a retrieve without `where` reached no check. Both entities are now resolved like a parameter type: a level the script states, else the stored one; a Usable entity, an ordinary module and the add-on module itself stay clean.
- **A pluggable widget's text-template parameter across an association builds, and describes so it re-executes** (mendixlabs/mxcli#1494) — `fieldset fs (legend: '{1}', legendParams: ({1} = M.Parent_Child/Label))` passed `check` and `exec` and failed the build with `[CE1365] "Attribute 'M.Child.Label' is not an attribute of entity 'M.Parent'."`: the parameter was written with its attribute but without the association hop, so mxbuild read the attribute against the widget's own entity. The same writer serves an object-list item's template (an Accordion group's `headerText`) and a Data grid 2 column's dynamic text, and all three failed; on Mendix 11.12.4 they now build with 0 errors. `describe page` printed such a parameter as the bare attribute (`{1} = Label`), which re-executed into CE1613 "The selected attribute 'M.Parent.Label' no longer exists"; it now prints `M.Parent_Child/Label`. The companion report, an expression parameter (`{1} = if 1 = 1 then 'A' else 'B'`) stored as an attribute name (mendixlabs/mxcli#1495), was fixed in v0.25.0 (#823) and is now pinned by a test.
- **`alter entity … add event handler` adds a second handler for the same event** (mendixlabs/mxcli#1540) — `add event handler on before commit call M.Second($currentObject)` failed with "event handler already exists for Before Commit" when the entity already had one, though Studio Pro allows several handlers for one event and `create or modify entity` with two `on before commit` clauses stores both (measured on 11.12.4: two Before Commit handlers are 0 errors, a void-returning one is CE7247, so the check does look at them). A handler is now identified by its moment, event and microflow: another microflow adds a second handler, the same microflow again is still a duplicate. The same key had made `add event handler if not exists … call M.New` skip — reporting "already exists" — whenever any handler for the event existed, and `drop event handler on before commit` remove whichever handler was stored first. `drop event handler` now takes `call M.Mf` to say which one; without it, a drop that matches several handlers is refused and names them.

Expand Down
13 changes: 12 additions & 1 deletion mdl/executor/validate_protected_module_members.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import (
// retrieve … where Payload = … Retrieve … attribute (and an association step)
// return $r/Payload, $i/AddOn.Assoc End event … attribute / association
//
// The entity of a create and of a database retrieve is checked here too: a
// Hidden entity is refused by both (Create object activity / Retrieve object(s)
// activity … is not allowed to use entity), even with no member named
// (mendixlabs/mxcli#1652).
//
// A member is reported only when it is established: its variable's entity is
// known, the member resolves on it (an association hop through
// associationTargetFrom), and its level is known — a level the script states,
Expand Down Expand Up @@ -55,6 +60,7 @@ func (r *protectedRefResolver) checkFlowMembers(name ast.QualifiedName, params [
switch s := st.(type) {
case *ast.CreateObjectStmt:
who := "create " + s.EntityType.String()
out = appendNonEmpty(out, r.entityRef(who, s.EntityType, "app modules"))
for _, ch := range s.Changes {
m, ok := r.changeMember(s.EntityType.String(), ch.Attribute)
report(who, m, ok)
Expand All @@ -71,7 +77,12 @@ func (r *protectedRefResolver) checkFlowMembers(name ast.QualifiedName, params [
who := "retrieve $" + s.Variable
if s.StartVariable != "" {
report(who, protectedMember{kind: "association", qn: s.Source.String()}, s.Source.Module != "")
} else if s.Where != nil {
break
}
// A database retrieve names the entity itself, so a Hidden one is
// refused with or without a where clause.
out = appendNonEmpty(out, r.entityRef(who, s.Source, "app modules"))
if s.Where != nil {
for _, steps := range xpathMemberPaths(s.Where) {
for _, m := range r.pathMembers(s.Source.String(), steps) {
report(who, m, true)
Expand Down
22 changes: 22 additions & 0 deletions mdl/executor/validate_protected_module_refs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,28 @@ func TestValidateProtectedModuleRefs(t *testing.T) {
{"open module hidden attribute", "create microflow MyFirstModule.C ($e: Open.E) begin change $e (Inner = 'x'); end;", nil},
{"untyped variable is silent", "create microflow MyFirstModule.C () begin change $nobody (Inner = 'x'); end;", nil},

// --- CE1406 on the entity of a create or a database retrieve (#1652) ---
{"retrieve of hidden entity",
"create microflow MyFirstModule.R () begin retrieve $l from MyAddOn.Request; end;",
[]string{"CE1406", "retrieve $l is not allowed to use entity 'MyAddOn.Request'", "app modules cannot reference hidden elements"}},
{"retrieve of hidden entity with where",
"create microflow MyFirstModule.R () begin retrieve $l from MyAddOn.Request where Payload = 'x'; end;",
[]string{"CE1406", "entity 'MyAddOn.Request'"}},
{"retrieve of usable entity", "create microflow MyFirstModule.R () begin retrieve $l from MyAddOn.Pub; end;", nil},
{"bare create of hidden entity",
"create microflow MyFirstModule.C () begin $r = create MyAddOn.Request; end;",
[]string{"CE1406", "create MyAddOn.Request is not allowed to use entity 'MyAddOn.Request'"}},
{"create of usable entity", "create microflow MyFirstModule.C () begin $p = create MyAddOn.Pub (Shown = 'x'); end;", nil},
{"retrieve of hidden entity made usable by the script",
"alter entity MyAddOn.Request set export level api;\ncreate microflow MyFirstModule.R () begin retrieve $l from MyAddOn.Request; end;", nil},
{"retrieve of hidden entity in a nanoflow",
"create nanoflow MyFirstModule.NF () begin retrieve $l from MyAddOn.Request; end;",
[]string{"CE1406", "entity 'MyAddOn.Request'"}},
{"add-on module retrieves its own hidden entity",
"create microflow MyAddOn.Own () begin retrieve $l from MyAddOn.Request; $r = create MyAddOn.Request; end;", nil},
{"open module retrieve and create of hidden entity",
"create microflow MyFirstModule.C () begin retrieve $l from Open.E; $e = create Open.E; end;", nil},

// --- controls: an ordinary module hides nothing ---
{"open module page", "create or modify navigation Responsive home page Open.P;", nil},
{"open module hidden microflow", "create microflow MyFirstModule.C () begin call microflow Open.MF(); end;", nil},
Expand Down
Loading