Skip to content

Commit 17b3da9

Browse files
akoclaude
andcommitted
fix(mcp): configure entity on context data view source (CE0488)
A data view bound to a page parameter (DataSource: $Param) authored over MCP opened in Studio Pro with CE0488 "No entity configured for the data source of this data view". mapDataViewSource used a mutually-exclusive switch: when the source had a ParameterName it wrote only sourceVariable (the Pages$PageVariable binding) and never entityRef, leaving the source with no entity. A real Studio Pro context data view writes BOTH entityRef and sourceVariable (see testdata/pg-page-contact-newedit.json). The executor already resolves the page parameter's entity into DataViewSource.EntityName, so the entity is available here. Set entityRef when EntityName is present and sourceVariable when ParameterName is present (no longer exclusive), matching the known-good shape. MCP-only — the MPR engines were never affected. DataGrids with database sources were already correct. Regression test in TestMapDataViewSource asserts a parameter+entity source carries both fields. Symptom table updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 70823aa commit 17b3da9

3 files changed

Lines changed: 31 additions & 9 deletions

File tree

.claude/skills/fix-issue.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ to the symptom table below, so the next similar issue costs fewer reads.
5757
| A page-level property can't be set — `ALTER PAGE X { SET PopupWidth = 800; }` (or any page-level prop other than Title/Url) fails with `unsupported page-level property: …` | The page-level SET handler only special-cased a couple of properties; everything else fell through to the default error | `mdl/backend/pagemutator/mutator.go` → `applyPageLevelSetMut` (shared by both engines) | Add a `case` writing the field at the top level of the Forms$Page doc via `dSetOrAppend` with the on-disk BSON type (int64 for PopupWidth/PopupHeight, bool for PopupResizable — verify against a Studio Pro page with `mxcli bson dump --format bson`). Page-level prop names are **case-sensitive**. For DESCRIBE roundtrip, emit the values back in the CREATE PAGE header. CREATE-time support: add a generic `IDENTIFIER COLON propertyValueV3` to `pageHeaderPropertyV3` (regen grammar), recognize the keys in `parsePageHeaderV3` (`applyGenericPageHeaderProp`, error on unknown), carry `*int`/`*bool` on `CreatePageStmtV3`, default to 600/600/false in `buildPageV3`, and have both writers honour `page.Popup*` (legacy `sdk/mpr/writer_pages.go` int64; codec `mdl/backend/modelsdk/page_write.go` int32 via gen — tolerated by mx check). The MCP backend has its **own** `mcpPageMutator` (pg content tree, not raw BSON) — page-level SET there reaches `SetWidgetProperty("")`; map it or reject honestly (it rejects, pending a `pg_read_page` probe of the pop-up keys). Issue #661 |
5858
| `Visible: [Attr != '']` / `Editable: […]` on a widget (CONTAINER, textbox, …) → CE0117 "Error(s) in expression." in Studio Pro on the legacy engine, and silently *ignored* on the modelsdk engine | Two bugs (NOT BSON structure): (1) the expression was emitted with a bare attribute reference (`Name != ''`) — a Mendix client expression must root attributes in the widget data context (`$currentObject/Name != ''`); (2) the modelsdk codec hardcoded `ConditionalVisibilitySettings` to null, dropping the expression entirely | `mdl/visitor/visitor_page_v3.go` → `buildConditionalExpression`/`conditionalExprToString`; `mdl/backend/modelsdk/widget_write.go` → `applyWidgetBase` + TypeDefaults | (1) For `VISIBLE`/`EDITABLE` xpath constraints, build the xpath AST and serialize via `conditionalExprToString`, which prefixes bare `IdentifierExpr` / non-variable-rooted `XPathPathExpr` with `$currentObject/` (leaves `$…`-rooted paths, literals, functions, enum qualified-names untouched). Do **not** apply this to datasource `where` clauses (those are real XPath). (2) In `applyWidgetBase`, type-assert `SetConditionalVisibilitySettings`/`SetConditionalEditabilitySettings` and emit a settings element when the model has one; register `Forms$ConditionalVisibilitySettings`/`…EditabilitySettings` TypeDefaults (`NullFields: Attribute,SourceVariable`; `MandatoryLists: Conditions[,ModuleRoles]`) so the null-when-unset slots stay null (the encoder only fills NullFields when not already emitted). **ALTER PAGE** had the same gap (`SET Visible = [expr]` parsed as a `propertyValueV3` array and silently no-op'd): add `VISIBLE/EDITABLE EQUALS xpathConstraint` alternatives to `alterPageAssignment` (regen grammar), route to `VisibleIf`/`EditableIf` in `buildAlterPageAssignment` (reusing `buildConditionalExpression`), and add `VisibleIf`/`EditableIf` cases to `setRawWidgetPropertyMut` (`mdl/backend/pagemutator/mutator.go`) that build the settings node via `setWidgetConditionalSettingMut` (rejecting editability on non-input widgets). The shared mutator covers both engines. Verify both engines with `mx check` = 0 errors. Issue #627 |
5959
| `visible: [$currentObject/Status = Mod.Enum.Value]` stored as `… = 'Value'` (string) → MxBuild CE0117 "Error(s) in expression" (v0.13.0 regression; both engines) | `conditionalExprToString` (added with #627) sent a qualified-enum `QualifiedNameExpr` to its `default` case → `xpathExprToString`, which converts a 3-part enum name to a string literal. Correct for an XPath *datasource* constraint (DB level), WRONG for a *client* visibility/editability expression (compares to the qualified enum value) | `mdl/visitor/visitor_page_v3.go``conditionalExprToString` | Add an explicit `*ast.QualifiedNameExpr` case returning `e.QualifiedName.String()` (the qualified literal) before the `default`. Leaves the datasource `where` path (`buildXPathString`/`xpathExprToString`) untouched, so DB-level enums still stringify to `'Value'`. Applies to CREATE and ALTER (both use `conditionalExprToString`). Verify the datasource enum still stores `'Value'` and the visibility enum stays qualified; `mx check` = 0 |
60+
| CE0488 "No entity configured for the data source of this data view" on a context (page-parameter-bound) data view authored over **MCP** — DESCRIBE looks correct (`dataview dvX (DataSource: $Param)`), but the model has no entity on the source. DataGrids with database sources are fine | `mapDataViewSource`'s `*pages.DataViewSource` case used a mutually-exclusive `switch`: when `ParameterName != ""` it wrote only `sourceVariable` and never `entityRef`. Studio Pro/pg write **both** entityRef AND sourceVariable for a context source (see `testdata/pg-page-contact-newedit.json`) | `mdl/backend/mcp/page_widgets.go``mapDataViewSource` | Drop the exclusive switch: set `entityRef` (DomainModels$DirectEntityRef) whenever `EntityName != ""` AND `sourceVariable` (Pages$PageVariable) whenever `ParameterName != ""`. The executor already resolves the parameter's entity into `EntityName` (`cmd_pages_builder_v3.go` parameter case), so both are available. MCP-only — the MPR engines were never affected |
6061
| Mendix can't resolve the microflow named in `CREATE ODATA CLIENT (ConfigurationMicroflow: microflow X.Y)` / `ErrorHandlingMicroflow:` — error names the literal string `"MICROFLOW X.Y"` as the missing microflow | Case-mismatched prefix strip: visitor emits uppercase `"MICROFLOW "` from `odataValueText`, but `extractMicroflowRef` only trimmed lowercase `"microflow "`, so the keyword survived into BSON | `mdl/executor/cmd_odata.go``extractMicroflowRef` | Use a case-insensitive strip: `if strings.EqualFold(ref[:10], "microflow ") { return ref[10:] }`. Whenever a value goes from a visitor that emits a keyword-prefixed form to an executor that strips it, the strip must match the case the visitor produces — grep visitor files for `"MICROFLOW " +`/`"ENTITY " +`/etc. when adding a new property. Issue #573 |
6162
| `describe`/catalog reports a numeric BSON field (Length, MinOccurs, MaxOccurs, MaxLength, FractionDigits, TotalDigits, Interval, NumberOfPagesToClose, …) as `0` / `unlimited` even though Studio Pro shows a real value | BSON numeric width mismatch — Studio Pro writes the field as `int64`, but the parser asserted `raw["X"].(int32)` so the type assertion failed silently and the field defaulted to its zero value | `sdk/mpr/parser_*.go` — grep for the field name; the fix point is the narrow assertion | Replace narrow type assertions on BSON numeric fields with the existing `extractInt(raw["X"])` helper (`sdk/mpr/parser.go`). It handles int32/int64/int/float64. When a non-zero default must survive a missing field, gate with `if _, ok := raw["X"]; ok { … = extractInt(...) }`. Sweep `grep -n '\.(int32)' sdk/mpr/parser_*.go` and ignore matches whose comment says "marker" (BSON-array-prefix probes are intentional). Issues #583, #585 |
6263
| Studio Pro shows a dropdown / property as its default value even though MDL set it explicitly (e.g. CREATE ODATA CLIENT with `ConfigurationMicroflow:` set, but the "Configuration source" dropdown reads "Constants only") | The BSON key mxcli writes isn't what Studio Pro reads — either the key name is wrong, or multiple dropdown options actually share a single field discriminated by something other than the key name (return type of a referenced microflow, sibling property, etc.) | The `serializeXxx` function in `sdk/mpr/writer_*.go` for the affected document type | (1) Ask the user to duplicate the offending object in Studio Pro and **explicitly pick each dropdown option** on the duplicate(s). An unconfigured duplicate just looks like "Constants only" and tells you nothing about which field the option uses. (2) Re-dump the duplicates from `mprcontents/**/*.mxunit` **after every Studio Pro change** — cached `/tmp/svc-*.json` files go stale the instant the user edits the project (see [[feedback-refresh-bson-dumps]]). (3) Diff against mxcli's output to find the renamed key. (4) Don't assume one-state-per-key. The OData "Configuration microflow" / "Headers microflow" case stores BOTH options in the single `ConfigurationMicroflow` BSON field — Studio Pro picks the dropdown label from the referenced microflow's return type. When a discriminator like that exists, have both MDL keywords write to the same model field. Issues #573, #587 and 2026-05-27 unify-config-microflow fix |

mdl/backend/mcp/page_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,19 @@ func TestMapDataViewSource(t *testing.T) {
141141
if err != nil || pv["sourceVariable"] == nil {
142142
t.Fatalf("param source: %+v / %v", pv, err)
143143
}
144+
// context (parameter-bound) source with a resolved entity must carry BOTH the
145+
// sourceVariable binding AND entityRef — otherwise the data view has no entity
146+
// configured on its source (CE0488). Regression for the MCP page-authoring gap.
147+
ctx, err := mapDataViewSource(&pages.DataViewSource{ParameterName: "Product", EntityName: "MES.Product"})
148+
if err != nil {
149+
t.Fatalf("context source: %v", err)
150+
}
151+
if sv, _ := ctx["sourceVariable"].(map[string]any); sv["pageParameter"] != "Product" {
152+
t.Fatalf("context source missing/var wrong sourceVariable: %+v", ctx)
153+
}
154+
if ref, _ := ctx["entityRef"].(map[string]any); ref["entity"] != "MES.Product" {
155+
t.Fatalf("context source missing entityRef (CE0488): %+v", ctx)
156+
}
144157
// direct entity -> entityRef
145158
er, err := mapDataViewSource(&pages.DataViewSource{EntityName: "Sales.Order"})
146159
if err != nil {

mdl/backend/mcp/page_widgets.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -297,21 +297,29 @@ func mapListViewSource(ds pages.DataSource) (map[string]any, error) {
297297
func mapDataViewSource(ds pages.DataSource) (map[string]any, error) {
298298
switch s := ds.(type) {
299299
case *pages.DataViewSource:
300+
if s.ParameterName == "" && s.EntityName == "" {
301+
return nil, fmt.Errorf("data view source has neither a page parameter nor an entity")
302+
}
300303
src := map[string]any{"$Type": "Pages$DataViewSource", "forceFullObjects": false}
301-
switch {
302-
case s.ParameterName != "":
304+
// A context (parameter-bound) data view still needs its entity configured
305+
// ON the data source: Studio Pro writes entityRef AND the sourceVariable
306+
// binding together (see testdata/pg-page-contact-newedit.json). Setting
307+
// only sourceVariable leaves the source with no entity → CE0488 "No entity
308+
// configured for the data source of this data view". The executor resolves
309+
// the page parameter's entity into EntityName (cmd_pages_builder_v3.go), so
310+
// it is available here even when a parameter is the source.
311+
if s.EntityName != "" {
312+
src["entityRef"] = map[string]any{
313+
"$Type": "DomainModels$DirectEntityRef",
314+
"entity": s.EntityName,
315+
}
316+
}
317+
if s.ParameterName != "" {
303318
src["sourceVariable"] = map[string]any{
304319
"$Type": "Pages$PageVariable",
305320
"pageParameter": s.ParameterName,
306321
"useAllPages": false,
307322
}
308-
case s.EntityName != "":
309-
src["entityRef"] = map[string]any{
310-
"$Type": "DomainModels$DirectEntityRef",
311-
"entity": s.EntityName,
312-
}
313-
default:
314-
return nil, fmt.Errorf("data view source has neither a page parameter nor an entity")
315323
}
316324
return src, nil
317325
case *pages.MicroflowSource:

0 commit comments

Comments
 (0)