Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .claude/skills/fix-issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ cases for these three BSON types — they fell to `default: return nil`.
| `mdl/executor/cmd_microflows_format_action.go` | Added 3 formatter cases |
| `mdl/executor/cmd_microflows_format_listop_test.go` | Added 4 formatter tests |
| `sdk/mpr/parser_listoperation_test.go` | New file, 4 parser tests |
| `UPDATE SECURITY`, `CREATE ASSOCIATION` or any `GRANT` silently strips **inherited** members from a specialized entity's access rules; `GRANT` naming an inherited member reports success and persists nothing, so REVOKE+GRANT cannot repair it. `mx check` shows only CE0066, hiding the CE2729 "No read access to attribute" cascade until Studio Pro's Update security is clicked | Mendix inheritance is multi-table: all of a parent's attributes are members of the child, so an access rule needs a MemberAccess entry for every member, own **and** inherited, each qualified against the entity that **declares** it. Both the GRANT builder and `ReconcileMemberAccesses` enumerated only `entity.Attributes`, so an inherited reference matched nothing and was deleted as stale — and reconciliation runs **immediately after every GRANT**, deleting what the grant had just written correctly | `mdl/executor/entity_hierarchy.go` (`EntityMembers`), `mdl/executor/cmd_security_write.go` (`execGrantEntityAccess`), `mdl/backend/modelsdk/domainmodel_security_write.go` (`ReconcileMemberAccesses`, `attrRefBelongsTo`), `sdk/mpr/writer_security.go` (legacy engine) | Walk the generalization chain and qualify each member against its declaring entity; in the reconciler, only strip a reference qualified to **this** entity — an ancestor may live in another module or System, neither loaded there, so preserve what cannot be validated. **Two facts must be established against `mx check`, never inferred**: (a) the child-qualified form is CE1613 "attribute no longer exists" while the declaring-entity form validates clean; (b) `System.User`'s members are the exception — entities specialising it are *user entities* whose platform members Mendix manages, and listing them turns a clean rule into CE0066, while omitting `System.FileDocument`'s six members is CE0066 until all are present. **Generalisable**: when a post-write reconcile pass validates against a narrower model than the writer used, it will quietly undo correct writes — check what runs *after* a write before concluding the writer is at fault. Repro `mdl-examples/bug-tests/758-inherited-member-access.mdl`. Issues #758, #765 (umbrella; #451 is the same declaring-entity rule in the change-object writer) |
| `describe` (and `context` / `diff-local`) renders a Retrieve's XPath with only its **first** predicate group — `where A/B[EndDate = $X];` when the BSON holds `[A/B[EndDate = $X]][Status != 'Completed'][CompletionDate = empty]`. No warning; the output reads as a complete but materially *less restrictive* query, so correct defensive code looks buggy | The grammar's `xpathConstraint` rule matches ONE bracket group, and Mendix concatenates siblings. `ParseXPathConstraint` removes the error listeners, so ANTLR parsed group 1, left the rest on the token stream, and **still returned ok=true**; `enrichXPathConstraintForDescribe` treated that as a full parse and re-rendered only what came back. The `if !ok { return original }` fallback never fired | `mdl/visitor/visitor_xpath_public.go` (`ParseXPathConstraint`), `mdl/visitor/xpath_groups.go` (`SplitXPathPredicateGroups`), `mdl/executor/cmd_microflows_format_action.go` (`enrichXPathGroups`, and the render-path split) | Two layers. (1) Reject a partial parse — after the rule, require `stream.LA(1) == antlr.TokenEOF`; that alone stops the loss, since the caller then falls back to the stored string. (2) Split into top-level groups and enrich each, so enrichment still reaches groups after the first. The splitter must track **nesting depth and quoting**: a naive `][` split mangles a nested `[A/B[x = 1]]` and a literal containing `]`. **Generalisable**: a parser that silently accepts a prefix is worse than one that fails — any `ok` returned by a rule that can match less than its input must be checked against EOF before callers treat it as lossless. Repro `mdl-examples/bug-tests/772-xpath-constraint-groups.mdl`; A/B against a pre-fix binary on the same project shows the two dropped groups. Issue #772 |
| An import/export mapping over an entity created with `EXTENDS` maps only its **own** attributes; every inherited field shows unmapped in Studio Pro, and `mx check` reports CE1613 "The selected attribute 'Mod.Child.Attr' no longer exists". An inherited Boolean/DateTime element also gets `DataType=String` | The mapping builder prefixed the entity being mapped unconditionally (`attr = parentEntity + "." + attr`), but a member reference is qualified against the entity that **declares** it — the same rule as entity access rules (#758) and the change-object writer (#451). Separately `resolveAttributeType` scanned only the entity's own attributes and fell through to its `"String"` default | `mdl/executor/cmd_import_mappings.go` and `cmd_export_mappings.go` (both carry the same two lines), `mdl/executor/entity_hierarchy.go` (`ResolveMemberRef`, `ResolveMemberType`) | Route both sites through the generalization walk added for #758: `ResolveMemberRef` returns the declaring-entity reference and `ResolveMemberType` finds the type up the chain, each falling back to the old behaviour when the member cannot be resolved. **Watch for the sibling defect**: the old `resolveAttributeType` matched entities **by name across every domain model**, so a same-named entity in another module could win — resolve the module by name instead. **Generalisable**: when one rule has several call sites, a fix at one of them proves nothing about the others; grep for the *pattern* (`range entity.Attributes`, `parentEntity + "."`) rather than the reported symptom. Repro `mdl-examples/bug-tests/703-mapping-inherited-attributes.mdl`; A/B on the same project shows `Map703.Contract.DocName` (CE1613) become `Map703.DocumentBase.DocName`. Issue #703, umbrella #765 |
| `alter settings model JavaVersion = 'Java21'` on Mendix 11.12+ produces a project mxbuild refuses to **load**: `mx check` reports `System.ArgumentOutOfRangeException ... (Parameter 'majorVersion is an unsupported value: Java21')` at `JavaVersionExtensions.fromString`. Every check downstream of the settings unit is lost with it | Mendix renamed the property between 11.6 (`JavaVersion` = `"Java21"`) and 11.12 (`JavaMajorVersion` = `"21"`) — and the rename changed the **value format** as well as the key. The #759 fix followed only the key, writing the caller's value through verbatim, so the 11.6 spelling landed in the 11.12 key | `mdl/settingsoverlay/settingsoverlay.go` (`JavaVersionValue`, `SetJavaVersion`) — shared by both engines; the dead third copy in `modelsdk/mpr/serialize_services.go` carried it too | Render the value in the dialect the stored key expects: strip/add the `Java` prefix per key, and pass an unrecognisable value through untouched so a typo surfaces as a Mendix error instead of a mangled setting. **Generalisable**: a renamed property is not only a renamed key — check whether the value encoding moved with it, and cover *both* directions (either spelling in, document's dialect out). Note the sharper failure mode: the original #759 shape was an unknown property, which mxbuild **tolerates**, so only Studio Pro broke; a wrong *value* for a known enum is a hard build failure, which is why this one surfaced as a red nightly rather than a user report. Repro `mdl-examples/bug-tests/759-java-version-value-dialect.mdl`. Issue #759 (follow-up) |

**Key insight:** `microflows$ListRange` stores offset/limit inside a nested
`CustomRange` map — must cast `raw["CustomRange"].(map[string]any)` before
Expand Down
9 changes: 9 additions & 0 deletions .claude/skills/mendix/generate-domain-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,15 @@ create persistent entity Module.Photo (

**Note:** `mxcli syntax entity` output may show EXTENDS after `)` — this is misleading. Always place EXTENDS before `(`.

**Security follows inheritance.** Mendix inheritance is multi-table: all of the
parent's attributes are members of the child, so a specialized entity's access rule
must cover them. Grant an inherited member exactly like one of the entity's own —
`grant Module.Viewer on Module.Attachment (read (AttachmentDescription, "Name", Size));`
— and `read *` / `write *` cover them too. Skipping them is Mendix CE0066 "Entity
access is out of date". The one exception is entities extending `System.User`, whose
inherited platform members Mendix manages and which must not be granted. See
`manage-security.md`.

#### System Attributes (Auditing)

Mendix supports four built-in auditing properties on persistent entities. Declare them as regular attributes using pseudo-types (like `autonumber`):
Expand Down
35 changes: 34 additions & 1 deletion .claude/skills/mendix/json-structures-and-mappings.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,40 @@ A JSON structure defines the schema of a JSON payload. It stores a JSON snippet
### Import Mappings
An import mapping converts a JSON string into Mendix entity objects. It maps JSON fields to entity attributes.

### Export Mappings
#### Inherited attributes

Mendix inheritance is multi-table: all of a parent's attributes are members of the
child, so an entity created with `extends` can map them. Name an inherited
attribute exactly like one of the entity's own — mxcli resolves each to the entity
that **declares** it, which is the reference Studio Pro needs to show the field
mapped.

```sql
create persistent entity Docs.DocumentBase (
DocName: String(200),
Confidential: Boolean
);

create persistent entity Docs.Contract extends Docs.DocumentBase (
ContractNumber: String(50)
);

create import mapping Docs.IMM_Contract
with json structure Docs.JSON_Contract
{
create Docs.Contract {
ContractNumber = contractNumber, -- own
DocName = docName, -- inherited
Confidential = confidential -- inherited
}
};
```

Qualifying an inherited attribute against the entity being mapped instead of its
declaring entity is Mendix **CE1613** "The selected attribute ... no longer
exists", and the field shows unmapped in Studio Pro.

## Export Mappings
An export mapping converts Mendix entity objects into a JSON string. It maps entity attributes to JSON fields.

### Critical: Import and Export Need Different Domain Models
Expand Down
52 changes: 52 additions & 0 deletions .claude/skills/mendix/manage-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,58 @@ revoke MyModule.User on MyModule.Customer (write (Email));
revoke MyModule.User on MyModule.Customer (delete);
```

#### Inherited members

Mendix inheritance is multi-table: a child adds attributes to its parent's, and
**all** the parent's members belong to the child. Grant them exactly like the
entity's own — `read *` / `write *` cover them too:

```sql
create persistent entity Docs.DocumentBase (
DocName: String(200),
Confidential: Boolean
);

create persistent entity Docs.Contract extends Docs.DocumentBase (
ContractNumber: String(50)
);

-- DocName is inherited, ContractNumber is Contract's own — name both the same way
grant Docs.Viewer on Docs.Contract (read (DocName, ContractNumber));

-- Attachment inherits the file members from System.FileDocument
create persistent entity Docs.Attachment extends System.FileDocument (
Category: String(50)
);
grant Docs.Viewer on Docs.Attachment (read (Category, "Name", Size));
```

An access rule must carry an entry for **every** member, own and inherited —
mxcli writes the ones you did not grant with rights `None`. Omitting them is
Mendix **CE0066** "Entity access is out of date", which masks the CE2729
"No read access to attribute" errors underneath until Studio Pro's *Update
security* is clicked.

A member name that matches nothing is now an error rather than a silent skip:

```
Error: entity Docs.Contract has no member(s) DocNam; grant only names members
of the entity or of an entity it inherits from
```

**Exception — user entities.** An entity extending `System.User` is a *user
entity*, and Mendix manages its inherited platform members (`Name`, `Password`,
`Blocked`, …). Those must **not** appear in the rule; listing them is CE0066.
Grant only the entity's own members — mxcli leaves the platform ones out
automatically:

```sql
create persistent entity Docs.Employee extends System.User (
EmployeeNo: String(20)
);
grant Docs.Viewer on Docs.Employee (read (EmployeeNo)); -- not Name/Blocked
```

### User Roles

```sql
Expand Down
11 changes: 10 additions & 1 deletion .claude/skills/mendix/project-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,21 @@ alter settings model BeforeShutdownMicroflow = 'Module.MF_Shutdown';
alter settings model HealthCheckMicroflow = 'Module.MF_HealthCheck';
alter settings model HashAlgorithm = 'BCrypt';
alter settings model BcryptCost = 12;
alter settings model JavaVersion = 'Java21';
alter settings model JavaVersion = 'Java21'; -- or '21'; see note below
alter settings model RoundingMode = 'HalfUp';
alter settings model AllowUserMultipleSessions = true;
alter settings model ScheduledEventTimeZoneCode = 'Etc/UTC';
```

**JavaVersion spelling.** Mendix renamed this property between versions: up to 11.6
it stores `JavaVersion` = `'Java21'`, from 11.12 it stores `JavaMajorVersion` =
`'21'`. Write either spelling — mxcli reads which one the project uses and stores
the value in that dialect. Getting this wrong is not a cosmetic difference: 11.12
parses the bare major and rejects the project outright with
`ArgumentOutOfRangeException: majorVersion is an unsupported value: Java21`.
`describe settings` always emits the project's own spelling, so its output replays
cleanly.

### Modify Configuration Settings

```sql
Expand Down
10 changes: 9 additions & 1 deletion cmd/mxcli/syntax/features_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,15 @@ func init() {
"show import mappings", "describe import mapping",
"with json structure", "find or create", "object handling",
},
Syntax: "SHOW IMPORT MAPPINGS [IN Module];\nDESCRIBE IMPORT MAPPING Module.Name;\nCREATE [OR MODIFY] IMPORT MAPPING Module.Name\n WITH JSON STRUCTURE Module.JsonStruct\n{\n create|find|find or create Module.Entity {\n Attr = jsonField [KEY],\n Assoc/Module.Child = nestedKey { ... }\n }\n};\nDROP IMPORT MAPPING Module.Name;\n\nOR MODIFY: updates mapping in-place, preserves UUID.",
Syntax: "SHOW IMPORT MAPPINGS [IN Module];\nDESCRIBE IMPORT MAPPING Module.Name;\n" +
"CREATE [OR MODIFY] IMPORT MAPPING Module.Name\n WITH JSON STRUCTURE Module.JsonStruct\n{\n" +
" create|find|find or create Module.Entity {\n Attr = jsonField [KEY],\n" +
" Assoc/Module.Child = nestedKey { ... }\n }\n};\nDROP IMPORT MAPPING Module.Name;\n\n" +
"OR MODIFY: updates mapping in-place, preserves UUID.\n\n" +
"Inherited attributes:\n" +
" An entity mapped with EXTENDS can map its inherited attributes too —\n" +
" name them exactly like its own. mxcli resolves each to the entity that\n" +
" declares it, which is what Studio Pro needs to show the field mapped.",
Example: "CREATE IMPORT MAPPING Shop.IMM_Order\n WITH JSON STRUCTURE Shop.JSON_Order\n{\n create Shop.Order {\n OrderId = orderId KEY,\n TotalAmount = total\n }\n};\n\n-- Idempotent update\nCREATE OR MODIFY IMPORT MAPPING Shop.IMM_Order\n WITH JSON STRUCTURE Shop.JSON_Order\n{\n find or create Shop.Order {\n OrderId = orderId KEY,\n TotalAmount = total,\n Status = status\n }\n};",
SeeAlso: []string{"export-mapping", "json-structure"},
})
Expand Down
20 changes: 18 additions & 2 deletions cmd/mxcli/syntax/features_security.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,24 @@ func init() {
"entity access", "grant", "revoke", "read", "write",
"create", "delete", "xpath", "row-level security",
},
Syntax: "GRANT <role> ON <module>.<entity> (<rights>) [WHERE '<xpath>'];\nREVOKE <role> ON <module>.<entity>;\nREVOKE <role> ON <module>.<entity> (<rights>);\n\nRights: CREATE, DELETE, READ *, READ (<attr>,...), WRITE *, WRITE (<attr>,...)",
Example: "GRANT Shop.Admin ON Shop.Customer (CREATE, DELETE, READ *, WRITE *);\nGRANT Shop.User ON Shop.Customer (READ *) WHERE '[Active = true()]';",
Syntax: "GRANT <role> ON <module>.<entity> (<rights>) [WHERE '<xpath>'];\n" +
"REVOKE <role> ON <module>.<entity>;\n" +
"REVOKE <role> ON <module>.<entity> (<rights>);\n\n" +
"Rights: CREATE, DELETE, READ *, READ (<attr>,...), WRITE *, WRITE (<attr>,...)\n\n" +
"Inherited members:\n" +
" Mendix inheritance is multi-table — a child adds attributes to its\n" +
" parent's, and ALL the parent's members belong to the child. Name them\n" +
" in a GRANT exactly like the entity's own; READ */WRITE * covers them\n" +
" too. A name that matches no member is an error, not a silent skip.\n\n" +
" Exception: entities extending System.User are user entities, whose\n" +
" platform members (Name, Password, Blocked, ...) Mendix manages. Do not\n" +
" grant those; mxcli leaves them out of the rule automatically.",
Example: "GRANT Shop.Admin ON Shop.Customer (CREATE, DELETE, READ *, WRITE *);\n" +
"GRANT Shop.User ON Shop.Customer (READ *) WHERE '[Active = true()]';\n\n" +
"-- Contract extends DocumentBase: DocName is inherited, ContractNumber is own\n" +
"GRANT Docs.Viewer ON Docs.Contract (READ (DocName, ContractNumber));\n\n" +
"-- Attachment extends System.FileDocument: Name and Size are inherited\n" +
"GRANT Docs.Viewer ON Docs.Attachment (READ (Category, \"Name\", Size));",
SeeAlso: []string{"security.module-role", "security.microflow-access"},
})

Expand Down
5 changes: 5 additions & 0 deletions docs-site/src/language/project-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ ALTER SETTINGS MODEL HashAlgorithm = 'BCrypt';
ALTER SETTINGS MODEL JavaVersion = '17';
```

Mendix renamed the Java version property between versions — up to 11.6 it is stored
as `JavaVersion` = `Java21`, from 11.12 as `JavaMajorVersion` = `21`. Write either
spelling (`'17'` or `'Java17'`): mxcli stores the value in whichever dialect the
project already uses.

### Configuration Settings

Server configuration settings like database type, URL, and HTTP port. Each configuration is identified by name (commonly `'default'`):
Expand Down
26 changes: 26 additions & 0 deletions docs-site/src/reference/integration/create-import-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,32 @@ CREATE OR MODIFY IMPORT MAPPING MyModule.IMM_Pet
- Arrays in the JSON sample map directly to child entity objects — there is no intermediate container entity (unlike export mappings).
- Import and export of the same JSON typically require different entity structures because of the FK direction difference.

## Inherited attributes

Mendix inheritance is multi-table: all of a parent's attributes are members of the
child, so an entity declared with `EXTENDS` can map them. Name an inherited
attribute exactly like one of the entity's own; mxcli resolves each to the entity
that **declares** it.

```sql
CREATE PERSISTENT ENTITY Docs.DocumentBase (DocName: String(200), Confidential: Boolean);
CREATE PERSISTENT ENTITY Docs.Contract EXTENDS Docs.DocumentBase (ContractNumber: String(50));

CREATE IMPORT MAPPING Docs.IMM_Contract
WITH JSON STRUCTURE Docs.JSON_Contract
{
create Docs.Contract {
ContractNumber = contractNumber,
DocName = docName,
Confidential = confidential
}
};
```

Referencing an inherited attribute against the entity being mapped rather than its
declaring entity is Mendix **CE1613** *"The selected attribute ... no longer
exists"*, and Studio Pro shows the field unmapped.

## See Also

[CREATE JSON STRUCTURE](create-json-structure.md), [CREATE EXPORT MAPPING](create-export-mapping.md)
Loading
Loading