Skip to content

forms: DynamicForm mistypes every array-valued schema key when 'schema' is assigned as a QVariant instead of bound #388

Description

@Yaraslaut

DynamicForm.qml types every field from p.type, and reaches the array form
({"type":["integer","null"]}, which is what schemaJson emits for a rule-3
strong id under $defs) through Array.isArray:

const types = Array.isArray(p.type) ? p.type : (p.type === undefined ? [] : [p.type])
...
isInteger: types.indexOf("integer") !== -1,
jsonType: types.length > 0 ? types[0] : ""

Array.isArray is false for a QVariantList. So when schema reaches the form
as a QVariant rather than as a JS object — an initial property, a
setProperty from C++, createTemporaryObject(..., {schema: ...}) — every
array-valued type is wrapped instead of unpacked, isInteger comes out false,
and the field falls through to the plain-text encoding: the id is submitted as a
quoted JSON string, which the server rejects with parse_number_failure.
That is the same symptom morph#189 fixed for the anyOf shape, reappearing
through a different door.

Verification status: reproduced, and not a shipped-app defect

Measured at 1f7a4619, Qt 6.11.2, QT_QPA_PLATFORM=offscreen, via a scratch
tst_*.qml through morph_forms_qml_tests -input <dir>, using
schemaJson<kanban::AddComment>() verbatim.

Declarative binding — the shape every shipped app uses
(schema: page.schemas["AddComment"] || ({}), and schema: JSON.parse(...)):

BOUND:  resolved.type=["integer","null"]  Array.isArray(resolved.type)=true
BOUND FIELD taskId  jsonType="integer"  isInteger=true
BOUND ready=true  previewLine={"taskId":7,"body":"hello"}      <- number. correct.

INLINE: resolved.type=["integer","null"]  Array.isArray(resolved.type)=true
INLINE FIELD taskId  jsonType="integer"  isInteger=true
INLINE ready=true  previewLine={"taskId":7,"body":"hello"}     <- number. correct.

Same schema, assigned as an initial property
(createTemporaryObject(formComponent, this, { schema: ... })):

AddComment FIELD taskId  jsonType=["integer","null"]  isInteger=false
AddComment ready: true
AddComment previewLine: {"taskId":"7","body":"hello"}          <- quoted. wrong.

Note jsonType in the broken case: it is the whole ["integer","null"] array,
which is types[0] when types == [["integer","null"]] — i.e. the
Array.isArray branch was not taken.

So no shipped rung is currently affected, and I am filing this as a
robustness gap rather than a live bug: every app binds schema declaratively.
What is affected is anything that hands the form a schema imperatively, which
is the natural thing for a C++-driven test to do and the thing I did first when
writing one (examples/kanban/tests/test_gui_forms_render.cpp reached the
correct result only because the schema goes through QML bindings there).

Not verified: whether x-rules, x-layout or any other array-valued schema
key degrades the same way under the same conversion. Very likely — the same
Array.isArray idiom appears at :186 (anyOf/oneOf branches), :249-252
(required), :411-431 (x-layout groups) — but I only measured type.

Why it is worth fixing rather than documenting

The failure is silent and type-shaped: no warning, no error, a ready form, and
a body the server refuses. A one-line helper (function asArray(v) { return Array.isArray(v) ? v : (v && typeof v.length === 'number' && typeof v !== 'string' ? Array.prototype.slice.call(v) : ...) }) or an explicit
JSON.parse(JSON.stringify(schema)) normalisation at the schema property
would remove a whole class of "works in the app, mistyped in the harness"
confusion.

What would change the verdict

Close when a DynamicForm given the same schema as a QVariantMap and as a JS
object produces identical fields and identical previewLine, asserted by a
test in src/qt/forms/tests/. Re-open if a later refactor reintroduces a bare
Array.isArray on schema data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: formsSubsystem: formstriage: rescopeReal problem, wrong framing; rewrite before building

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions