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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-16", "symptom": "mendixlabs/mxcli#1510: MDL089 on a doc comment above a create that stores no documentation told the user to \"Move the doc comment directly above `create json structure …`, after the `create or modify published rest service …`\" — moving text written for one element onto a different one.", "cause": "ValidateDetachedDocComments built the move suggestion from DetachedDocComment.Next for every statement. That is right for a drop/grant standing between a comment and its create (#877), but a create IS the element the comment describes; the next storing statement is a different element.", "file": "mdl/executor/validate_detached_doc_comments.go", "fix": "When the statement head is a create, the warning says MDL cannot store documentation for that element and suggests a `--` comment; the next-statement suggestion stays for drop/grant/revoke/set/alter.", "test": "mdl/executor TestValidateProgram_DetachedDocCommentOnCreate_Issue1510 (create module role followed by a create entity); control TestValidateProgram_DetachedDocComment keeps the drop suggestion. Revert check: with the create branch disabled the test reports the move-to-next-create suggestion.", "insight": "A suggestion derived from position (\"the next statement that can take it\") is only right when the position is the mistake. Ask what the comment was written FOR before telling the user where to put it.", "refs": ["mdl/executor/validate_detached_doc_comments_test.go"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-16", "symptom": "mendixlabs/mxcli#1510: lint QUAL002 reported import/export mappings, published REST services and data transformers as undocumented (45 findings on one project), and nothing in MDL could close them: a `/** … */` above `create or modify published rest service` was dropped with MDL089. Every published REST service was reported even when Studio Pro HAD documented it, and `create or modify` of a Studio Pro-documented mapping, published REST service or data transformer wrote an empty Documentation over the stored text.", "cause": "The four create statements' AST had no Documentation field, so storesDocumentation() reported the comment as detached and the visitor never read it. The executors never set the model's Documentation (the mapping writers wrote whatever the model held, i.e. \"\"; the data transformer writer wrote a literal \"\"; the published REST writer only carried the stored value via keepStoredTopLevel). model.PublishedRestService and model.DataTransformer had no Documentation, and the catalog builder wrote \"\" for published_rest_services.Documentation (\"not stored on published REST\").", "file": "mdl/ast/ast_import_export_mapping.go, mdl/ast/ast_rest.go, mdl/ast/ast_datatransformer.go, mdl/visitor/visitor_import_export_mapping.go, mdl/visitor/visitor_rest.go, mdl/visitor/visitor_datatransformer.go, mdl/executor/cmd_import_mappings.go, mdl/executor/cmd_export_mappings.go, mdl/executor/cmd_published_rest.go, mdl/executor/cmd_datatransformer.go, mdl/executor/documentation_carry.go, mdl/backend/modelsdk/published_rest_write.go, mdl/backend/modelsdk/integration_read.go, mdl/backend/modelsdk/data_transformer_write.go, mdl/catalog/builder_rest.go, model/types.go", "fix": "Documentation + DocumentationSet on the four statements, read with findDocComment; the executors set it and carry the stored value when no comment is written (carriedDocumentation, #1018); model + modelsdk read and write the top-level `Documentation` key for published REST services and data transformers (published REST drops it from the keep-stored list); describe prints it with writeDocumentDocComment (`--` lines when the text holds `*/`); the catalog stores the service's documentation. Java action parameter descriptions were already writable (#1588).", "test": "mdl/visitor TestIntegrationDocumentsReadDocComment_Issue1510; mdl/executor TestIntegrationDocumentation_Issue1510 (PedApp copy: stored key, describe round trip, carry, `/** */` clears) and TestIntegrationDocumentation_StudioProMappingKeepsIt_Issue1510; mdl/catalog TestPublishedRestServiceDocumentationReachesCatalog_Issue1510; the integration TestDocumentation_SurvivesRewrite gains the four doctypes. Revert check: without the fix every new test fails with the reported symptom; stubbing only the carry fails the rewrite and Studio Pro cases.", "insight": "Verify the BSON key against Mendix's own reader, not only modelsdk/gen: `mx dump-mpr --module-names=X` prints the model as Mendix deserialises it, so the doc comment appearing as the unit's `documentation` there (11.6.6 and 11.15) proves the key is the one Studio Pro reads. A QUAL002 finding nobody can close has two suspects, the writer and the catalog reader, and here both were broken: the catalog's hard-coded \"\" made the rule fire on Studio Pro-documented services too.", "refs": ["mdl-examples/bug-tests/1510-documentation-for-mappings-rest-javaparams.mdl", "mdl/executor/integration_documentation_issue1510_test.go", "mdl/visitor/visitor_integration_documentation_test.go", "mdl/catalog/builder_rest_documentation_test.go"]}
6 changes: 3 additions & 3 deletions .claude/skills/mendix/check-syntax/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ keeps the header off a file it would make refuse (ako/mxcli#876).

A `/** … */` doc comment documents the statement right after it, and only a
`create` of something with documentation stores one. Above a `drop`, `grant`,
`revoke`, `set`, `alter` or `create module` it is ignored, so **MDL089** warns
and names the next statement that could have taken it. In drop-then-create, put
the comment between the `drop` and the `create` (ako/mxcli#877).
`revoke`, `set` or `alter` it is ignored, so **MDL089** warns and names the next
statement that could take it — in drop-then-create, put it between the two
(ako/mxcli#877). Above a `create` that stores none, make it a `--` comment.

### It resolves MEMBER names too, where it can establish the entity

Expand Down
43 changes: 30 additions & 13 deletions .claude/skills/mendix/json-structures-and-mappings/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ description: "Create and manage JSON structures, import mappings and export mapp

| lines | section |
|---|---|
| 32-109 | Key Concepts |
| 110-121 | Export Mappings |
| 122-186 | JSON Structures |
| 187-228 | Message Definitions |
| 229-389 | Import Mappings |
| 390-487 | Export Mappings |
| 488-505 | Starting a Mapping Below the Payload Root |
| 506-575 | Microflow Actions |
| 576-588 | Browse |
| 589-636 | Export Workflow: PE → NPE → JSON |
| 637-643 | Realistic Example: Countries REST API |
| 644-667 | Placing Documents in Folders |
| 668-682 | Common Mistakes |
| 33-110 | Key Concepts |
| 111-122 | Export Mappings |
| 123-187 | JSON Structures |
| 188-229 | Message Definitions |
| 230-390 | Import Mappings |
| 391-488 | Export Mappings |
| 489-506 | Starting a Mapping Below the Payload Root |
| 507-576 | Microflow Actions |
| 577-589 | Browse |
| 590-637 | Export Workflow: PE → NPE → JSON |
| 638-644 | Realistic Example: Countries REST API |
| 645-668 | Placing Documents in Folders |
| 669-684 | Documentation |
| 685-699 | Common Mistakes |

<!-- /skill-index -->
# JSON Structures, Import Mappings & Export Mappings
Expand Down Expand Up @@ -665,6 +666,22 @@ adding a folder to an existing script is safe and removing one is a no-op.

See `organize-project` for `move` and the full folder story.

## Documentation

A `/** … */` doc comment directly above the `create` is the document's
documentation — for JSON structures, import and export mappings alike (and for
published REST services and data transformers). It is what `lint` QUAL002 asks
for, and `describe` prints it back. On `create or modify`, **no** comment keeps
the stored text and an empty `/** */` clears it:

```mdl
mdl 1;
/** Maps the order payload of the shop API onto Sales.Order. */
create or modify import mapping Sales.IMM_Order
with json structure Sales.JSON_Order
{ create Sales.Order { OrderId = id, Total = total } };
```

## Common Mistakes

| Mistake | Fix |
Expand Down
14 changes: 11 additions & 3 deletions cmd/mxcli/syntax/features_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ func init() {
"basic authentication", "active session", "authentication microflow",
"authentication parameter", "header parameter", "api key", "token",
},
Syntax: "CREATE [OR MODIFY] PUBLISHED REST SERVICE Module.Name (\n Path: 'rest/api/v1',\n Version: '1.0.0',\n ServiceName: 'My API',\n [Authentication: none | ( basic, session, microflow Module.Authenticate [( $Param: query|header ['HttpName'] [description '...'], ... )] )]\n)\n{\n RESOURCE 'name' {\n GET '' MICROFLOW Module.GetAll;\n GET '{id}' MICROFLOW Module.GetById;\n POST '' MICROFLOW Module.Create\n [IMPORT MAPPING Module.IMM] [EXPORT MAPPING Module.EMM]\n [COMMIT Yes | YesWithoutEvents | No]; -- no COMMIT clause: Yes\n }\n};\n\n" +
Syntax: "[/** documentation */]\nCREATE [OR MODIFY] PUBLISHED REST SERVICE Module.Name (\n Path: 'rest/api/v1',\n Version: '1.0.0',\n ServiceName: 'My API',\n [Authentication: none | ( basic, session, microflow Module.Authenticate [( $Param: query|header ['HttpName'] [description '...'], ... )] )]\n)\n{\n RESOURCE 'name' {\n GET '' MICROFLOW Module.GetAll;\n GET '{id}' MICROFLOW Module.GetById;\n POST '' MICROFLOW Module.Create\n [IMPORT MAPPING Module.IMM] [EXPORT MAPPING Module.EMM]\n [COMMIT Yes | YesWithoutEvents | No]; -- no COMMIT clause: Yes\n }\n};\n\n" +
"-- Operation parameters come from the microflow, as Studio Pro derives them:\n" +
"-- a parameter named in the path ('{id}') -> path parameter\n" +
"-- an object or a list -> the body\n" +
Expand All @@ -374,6 +374,9 @@ func init() {
"-- list only when it differs from that default.\n" +
"-- With authentication on, grant at least one module role (CE0338);\n" +
"-- custom authentication also needs app security on (CE6600).\n\n" +
"-- Documentation: the /** ... */ doc comment above CREATE, the service's own\n" +
"-- documentation (not the OpenAPI description, which MDL keeps unchanged).\n" +
"-- On OR MODIFY no comment keeps the stored text, /** */ clears it.\n\n" +
"ALTER PUBLISHED REST SERVICE Module.Name SET ( Version: '2.0.0', Authentication: (session) );\nALTER PUBLISHED REST SERVICE Module.Name SET Version = '2.0.0';\nALTER PUBLISHED REST SERVICE Module.Name ADD RESOURCE 'items' { ... };\nALTER PUBLISHED REST SERVICE Module.Name DROP RESOURCE 'legacy';\nDROP PUBLISHED REST SERVICE Module.Name;",
Example: "mdl 1;\nCREATE PUBLISHED REST SERVICE Module.OrderAPI (\n Path: 'rest/orders/v1',\n Version: '1.0.0',\n ServiceName: 'Order API',\n Authentication: (basic, session)\n)\n{\n RESOURCE 'orders' {\n GET '' MICROFLOW Module.GetAllOrders;\n GET '{id}' MICROFLOW Module.GetOrderById;\n POST '' MICROFLOW Module.CreateOrder;\n DELETE '{id}' MICROFLOW Module.DeleteOrder;\n }\n};\n\nGRANT ACCESS ON PUBLISHED REST SERVICE Module.OrderAPI\n TO Module.User, Module.Admin;",
SeeAlso: []string{"rest", "rest.consumed"},
Expand Down Expand Up @@ -784,6 +787,7 @@ DESCRIBE DATABASE CONNECTION Ops.Erp;`,
"object handling", "converter", "value transform", "custom handling", "by microflow",
},
Syntax: "LIST IMPORT MAPPINGS [IN Module];\nDESCRIBE IMPORT MAPPING Module.Name;\n" +
"[/** documentation */]\n" +
"CREATE [OR MODIFY] IMPORT MAPPING Module.Name [FOLDER 'path']\n" +
" WITH JSON STRUCTURE Module.JsonStruct [ROOT a/b/c]\n" +
" | WITH MESSAGE DEFINITION Module.Collection.Definition\n" +
Expand All @@ -794,6 +798,8 @@ DESCRIBE DATABASE CONNECTION Ops.Erp;`,
" Attr = a/b/c,\n" +
" Assoc/Module.Child = nestedKey { ... }\n }\n};\nDROP IMPORT MAPPING Module.Name;\n\n" +
"OR MODIFY: updates mapping in-place, preserves UUID.\n\n" +
"Documentation: the /** ... */ doc comment above CREATE (QUAL002 asks for\n" +
"it). On OR MODIFY no comment keeps the stored text, /** */ clears it.\n\n" +
"Nested members (Attr = a/b/c):\n" +
" Reaches a leaf several levels down with NO entity for the levels in\n" +
" between — the shape Studio Pro produces when you tick a nested leaf\n" +
Expand Down Expand Up @@ -848,7 +854,9 @@ DESCRIBE DATABASE CONNECTION Ops.Erp;`,
"with json structure", "with message definition", "null values",
"as jsonKey", "converter", "value transform",
},
Syntax: "LIST EXPORT MAPPINGS [IN Module];\nDESCRIBE EXPORT MAPPING Module.Name;\nCREATE [OR MODIFY] EXPORT MAPPING Module.Name [FOLDER 'path']\n WITH JSON STRUCTURE Module.JsonStruct [ROOT a/b/c]\n | WITH MESSAGE DEFINITION Module.Collection.Definition\n | WITH XML SCHEMA Module.Schema\n [NULL VALUES LeaveOutElement|SendAsNil]\n{\n Module.Entity {\n jsonField = Attr,\n jsonField = Module.MF(Attr), -- value transform\n group AS key { ... }, -- entity-less grouping node\n Assoc/Module.Child AS nestedKey { ... }\n }\n};\nDROP EXPORT MAPPING Module.Name;\n\nOR MODIFY: updates mapping in-place, preserves UUID.\n\n" +
Syntax: "LIST EXPORT MAPPINGS [IN Module];\nDESCRIBE EXPORT MAPPING Module.Name;\n[/** documentation */]\nCREATE [OR MODIFY] EXPORT MAPPING Module.Name [FOLDER 'path']\n WITH JSON STRUCTURE Module.JsonStruct [ROOT a/b/c]\n | WITH MESSAGE DEFINITION Module.Collection.Definition\n | WITH XML SCHEMA Module.Schema\n [NULL VALUES LeaveOutElement|SendAsNil]\n{\n Module.Entity {\n jsonField = Attr,\n jsonField = Module.MF(Attr), -- value transform\n group AS key { ... }, -- entity-less grouping node\n Assoc/Module.Child AS nestedKey { ... }\n }\n};\nDROP EXPORT MAPPING Module.Name;\n\nOR MODIFY: updates mapping in-place, preserves UUID.\n\n" +
"Documentation: the /** ... */ doc comment above CREATE, as on an import\n" +
"mapping. On OR MODIFY no comment keeps the stored text, /** */ clears it.\n\n" +
"Grouping nodes (group as key { ... }):\n" +
" A JSON object with no Mendix object behind it — Studio Pro's\n" +
" entity-less object element. It may hold OBJECT elements only: a\n" +
Expand All @@ -874,7 +882,7 @@ DESCRIBE DATABASE CONNECTION Ops.Erp;`,
"data transformer", "create data transformer", "drop data transformer",
"list data transformers", "jslt", "xslt", "transform",
},
Syntax: "LIST DATA TRANSFORMERS [IN Module];\nDESCRIBE DATA TRANSFORMER Module.Name;\nCREATE [OR MODIFY] DATA TRANSFORMER Module.Name [FOLDER 'path']\n SOURCE JSON '{ ... }'\n{\n JSLT 'single-line-expression';\n -- or multi-line:\n JSLT $$\n{ ... }\n $$;\n};\nDROP DATA TRANSFORMER Module.Name;\n\nOR MODIFY: updates transformer in-place, preserves UUID.",
Syntax: "LIST DATA TRANSFORMERS [IN Module];\nDESCRIBE DATA TRANSFORMER Module.Name;\n[/** documentation */]\nCREATE [OR MODIFY] DATA TRANSFORMER Module.Name [FOLDER 'path']\n SOURCE JSON '{ ... }'\n{\n JSLT 'single-line-expression';\n -- or multi-line:\n JSLT $$\n{ ... }\n $$;\n};\nDROP DATA TRANSFORMER Module.Name;\n\nOR MODIFY: updates transformer in-place, preserves UUID.\n\nDocumentation: the /** ... */ doc comment above CREATE. On OR MODIFY no\ncomment keeps the stored text, /** */ clears it.",
Example: "mdl 1;\nCREATE DATA TRANSFORMER ETL.FlattenOrder\n SOURCE JSON '{\"order\": {\"id\": 1, \"total\": 99.0}}'\n{\n JSLT '{\"id\": .order.id, \"total\": .order.total}';\n};\n\n-- Multi-line JSLT\nCREATE OR MODIFY DATA TRANSFORMER ETL.WeatherSummary\n SOURCE JSON '{\"current\": {\"temp\": 12.8, \"wind\": 18.3}}'\n{\n JSLT $$\n{\n \"temperature\": .current.temp,\n \"wind_speed\": .current.wind\n}\n $$;\n};",
SeeAlso: []string{"integration"},
})
Expand Down
3 changes: 3 additions & 0 deletions docs-site/src/examples/rest-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ CREATE OR MODIFY CONSUMED REST SERVICE Integration.OrdersApi (
Create a published REST service with CRUD operations backed by microflows.

```sql
/** Order management API for the shop front end. */
CREATE PUBLISHED REST SERVICE Module.OrderAPI (
Path: 'rest/orders/v1',
Version: '1.0.0',
Expand All @@ -325,6 +326,8 @@ CREATE PUBLISHED REST SERVICE Module.OrderAPI (
};
```

**Documentation:** the `/** … */` doc comment above `CREATE` is the service's documentation — what `lint` QUAL002 asks for. On `CREATE OR MODIFY` no comment keeps the stored text and `/** */` clears it. The OpenAPI description consumers read (`PublicDocumentation`) has no MDL spelling and is kept as stored.

**Operation paths:** Use empty string `''` for the root, `'{paramName}'` for path parameters. Do NOT start or end with `/`. Path parameters must match a microflow parameter name exactly (case-sensitive) — `'{id}'` binds the microflow's `$id`, whatever its type.

**Operation parameters** come from the microflow, the way Studio Pro derives them. Create the microflow before the service:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Synopsis

```sql
[ /** description */ ]
CREATE [ OR MODIFY ] DATA TRANSFORMER module.Name
[ FOLDER 'folder_path' ]
SOURCE { JSON | XML } 'sample'
Expand Down Expand Up @@ -41,6 +42,9 @@ The `SOURCE` clause provides a representative input document. Mendix uses it to
`FOLDER 'folder_path'`
: Optional. Places the document in the named module folder, creating missing folders in the path. On `CREATE OR MODIFY` this **moves** an existing document; omitting the clause leaves placement alone rather than returning the document to the module root. See [MOVE](../organization/move.md).

`/** description */`
: Optional. The documentation of the data transformer, as a doc comment before the statement. `lint` QUAL002 reports a data transformer without it, and `DESCRIBE` prints it back. On `CREATE OR MODIFY`, no doc comment keeps the stored documentation and an empty `/** */` clears it.

`SOURCE JSON 'sample'`
: A representative JSON input document. Multi-line samples can use `$$...$$` quoting.

Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/reference/integration/create-export-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Synopsis

```sql
[ /** description */ ]
CREATE [ OR MODIFY ] EXPORT MAPPING module.Name
[ FOLDER 'folder_path' ]
[ WITH JSON STRUCTURE module.JsonStructure ]
Expand Down Expand Up @@ -57,6 +58,9 @@ If `OR MODIFY` is specified and the mapping already exists, it is updated in pla
`FOLDER 'folder_path'`
: Optional. Places the document in the named module folder, creating missing folders in the path. On `CREATE OR MODIFY` this **moves** an existing document; omitting the clause leaves placement alone rather than returning the document to the module root. See [MOVE](../organization/move.md).

`/** description */`
: Optional. The documentation of the export mapping, as a doc comment before the statement. `lint` QUAL002 reports a export mapping without it, and `DESCRIBE` prints it back. On `CREATE OR MODIFY`, no doc comment keeps the stored documentation and an empty `/** */` clears it.

`WITH JSON STRUCTURE module.JsonStructure`
: Associates the mapping with the named JSON structure.

Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/reference/integration/create-import-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Synopsis

```sql
[ /** description */ ]
CREATE [ OR MODIFY ] IMPORT MAPPING module.Name
[ FOLDER 'folder_path' ]
[ WITH JSON STRUCTURE module.JsonStructure ]
Expand Down Expand Up @@ -80,6 +81,9 @@ If `OR MODIFY` is specified and the mapping already exists, it is updated in pla
`FOLDER 'folder_path'`
: Optional. Places the document in the named module folder, creating missing folders in the path. On `CREATE OR MODIFY` this **moves** an existing document; omitting the clause leaves placement alone rather than returning the document to the module root. See [MOVE](../organization/move.md).

`/** description */`
: Optional. The documentation of the import mapping, as a doc comment before the statement. `lint` QUAL002 reports a import mapping without it, and `DESCRIBE` prints it back. On `CREATE OR MODIFY`, no doc comment keeps the stored documentation and an empty `/** */` clears it.

`WITH JSON STRUCTURE module.JsonStructure`
: Associates the mapping with the named JSON structure. The structure defines the JSON shape.

Expand Down
Loading
Loading