Skip to content

fix: mappings skipped attributes inherited from a parent entity (#703) - #82

Merged
ako merged 2 commits into
mainfrom
claude/mapping-inherited-attributes-703
Aug 2, 2026
Merged

fix: mappings skipped attributes inherited from a parent entity (#703)#82
ako merged 2 commits into
mainfrom
claude/mapping-inherited-attributes-703

Conversation

@ako

@ako ako commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Fixes mendixlabs/mxcli#703 — the mapping half of the #765 umbrella, following mendixlabs#758 (access rules).

Problem

Mendix inheritance is multi-table: a child adds attributes to its parent's, and all the parent's are members of the child. A mapping element bound to one must reference the entity that declares it. The builder prefixed the entity being mapped, unconditionally:

attr := def.Attribute
if parentEntity != "" && !strings.Contains(attr, ".") {
    attr = parentEntity + "." + attr      // always the CHILD
}

So every inherited field produced a reference to an attribute that entity doesn't have. Studio Pro shows the field unmapped — the reported symptom — and mx check reports CE1613 "The selected attribute … no longer exists".

A second, quieter defect sat next to it. resolveAttributeType scanned only the entity's own attributes and fell through to a "String" default, so an inherited Boolean or DateTime element carried the wrong DataType even once the reference was correct. Worth noting because fixing only the reference would have looked like a complete fix while silently mistyping every inherited non-String field.

That function also matched entities by name across every domain model, ignoring the module — a same-named entity elsewhere could win. It now resolves the module by name.

Approach

Both the import and export builders carried the same two lines; both are fixed. They route through the generalization walk added for mendixlabs#758, generalised here into:

  • ResolveMemberRef — the declaring-entity reference for a member
  • ResolveMemberType — the member's type, found up the chain

Each falls back to the previous behaviour when the member can't be resolved, so an unresolvable name behaves exactly as before rather than changing shape. EntityMembersFor now takes the backend directly, since the mapping builders hold no ExecContext.

Verification

End-to-end on a real 11.12.2 project — one own and two inherited attributes, mapped in both directions:

reference DataType
before Map703.Contract.DocName → CE1613 StringType
Map703.Contract.Confidential → CE1613 StringType
after Map703.DocumentBase.DocName StringType
Map703.DocumentBase.Confidential BooleanType

mx check0 errors (was 2 × CE1613). Full ./... suite green.

Both halves mutation-checked — including one detail worth flagging for review: my first mutation of the type fix passed, because the unit test exercised ResolveMemberType directly while the mutation disabled its call site in resolveAttributeType. I added a test at the call site, which does fail under that mutation. A resolver test proves the resolver, not that anything uses it.

Repro script: mdl-examples/bug-tests/703-mapping-inherited-attributes.mdl (covers import and export).

Documentation

Inheritance was unmentioned in every mapping doc, so this adds it to mxcli syntax import-mapping, the json-structures-and-mappings skill, and docs-site create-import-mapping.md.

Generalisable lesson (recorded in the symptom table)

When one rule has several call sites, a fix at one proves nothing about the others — grep for the pattern (range entity.Attributes, parentEntity + ".") rather than the reported symptom. This is the third site of the same declaring-entity rule; mendixlabs#451's was already fixed independently in the microflow path, which is exactly why mendixlabs#765 was filed as an umbrella.

With this, mendixlabs#765's three covered issues are all closed: mendixlabs#758 (access rules), mendixlabs#703 (mappings), mendixlabs#451 (change-object writer).


Generated by Claude Code

claude added 2 commits August 2, 2026 05:32
…ixlabs#703)

Mendix inheritance is multi-table: a child adds attributes to its parent's, and
all the parent's are members of the child. A mapping element bound to one must
reference the entity that DECLARES it. The builder prefixed the entity being
mapped, unconditionally:

    attr := def.Attribute
    if parentEntity != "" && !strings.Contains(attr, ".") {
        attr = parentEntity + "." + attr      // always the CHILD
    }

so every inherited field produced a reference to an attribute that entity does
not have. Studio Pro shows the field unmapped — the reported symptom — and
mx check reports CE1613 "The selected attribute ... no longer exists".

A second, quieter defect sat next to it: resolveAttributeType scanned only the
entity's own attributes and fell through to a "String" default, so an inherited
Boolean or DateTime element carried the wrong DataType even once the reference
was correct. That function also matched entities by name across every domain
model, ignoring the module, so a same-named entity elsewhere could win; it now
resolves the module by name.

Both the import and export builders carried the same two lines, and both are
fixed. They route through the generalization walk added for mendixlabs#758, generalised
here into ResolveMemberRef (declaring-entity reference) and ResolveMemberType
(type from up the chain), each falling back to the previous behaviour when the
member cannot be resolved. EntityMembersFor takes the backend directly so the
mapping builders, which hold no ExecContext, can use it.

This closes the mapping half of the mendixlabs#765 umbrella; the same declaring-entity rule
governs entity access rules (mendixlabs#758) and the change-object writer (mendixlabs#451).

Verified end-to-end on a real 11.12.2 project with an entity extending another,
mapping one own and two inherited attributes in both directions:

    before:  Map703.Contract.DocName        StringType   -> CE1613
             Map703.Contract.Confidential   StringType   -> CE1613
    after:   Map703.DocumentBase.DocName        StringType
             Map703.DocumentBase.Confidential   BooleanType
             mx check -> 0 errors

Both halves mutation-checked, including a test at the resolveAttributeType call
site rather than only on the resolver — reverting the call site alone left the
resolver's own test green.

Docs: inheritance was unmentioned in every mapping doc, so the syntax topic, the
json-structures-and-mappings skill and docs-site create-import-mapping now cover
it.

Refs mendixlabs#703, mendixlabs#765
The doctype example granted read on SecTest.Customer (Notes), but the entity
only declares Name, Email and IsActive. Before mendixlabs#758 an unmatched member name was
dropped in silence, so the grant did nothing and the script still passed; with
that silence replaced by an error the example fails, and the integration tier
caught it.

The example is what is wrong: its own comment says "adding Notes access
preserves existing Name and Email", so it always meant to demonstrate an
additive grant on a third attribute. Declaring Notes makes it do that.

Fixes the build-and-test failure on main introduced by #81.
@ako
ako merged commit 32b5dd8 into main Aug 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants