Skip to content

fix: support anyOf/type-less JSON schemas in key/config creation UI - #1152

Open
knutties wants to merge 3 commits into
mainfrom
fix/anyof-schema-key-creation
Open

knutties wants to merge 3 commits into
mainfrom
fix/anyof-schema-key-creation

Conversation

@knutties

@knutties knutties commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Problem

Creating a Default Config key (the single key creation UI) was impossible when the type schema had no top-level type field — e.g. a JSON Schema combinator (anyOf/oneOf/allOf/$ref) or a bare const.

Example schema from the report:

{ "anyOf": [ { "type": "string" }, { "const": "default_str_ignore_this" } ] }

SchemaType::try_from errored with type not defined in schema, and the Default Value input was rendered disabled, so no value could be entered and the key could not be saved. The same gap affected the context, override, and variant forms and the default-config detail page.

Root cause

SchemaType::try_from (crates/frontend/src/schema.rs) hard-required a top-level type. That SchemaType is only used to choose which input widget to render — the value itself is validated against the full JSON Schema on the backend (jsonschema). So a missing top-level type should not be a hard failure.

Fix

Add a SchemaType::Any variant, returned by try_from when the schema object has no type. It:

  • maps to a free-form Monaco/JSON editor (InputType::Monaco)
  • parses input as arbitrary JSON (serde_json::from_str::<Value>)

Genuinely malformed schemas (present-but-invalid type, e.g. "type": "foobar") still error as before. Because the match arms on SchemaType are exhaustive/compiler-enforced, this fixes every form that consumes it in one place.

Files

  • crates/frontend/src/schema.rs — new Any variant; try_from returns Ok(Any) on missing type; default_value arm; unit tests
  • crates/frontend/src/components/input.rsInputType::from → Monaco for Any; parse_input parses free-form JSON; unit tests
  • crates/frontend/src/components/override_form.rsTypeBadge renders an any badge
  • crates/frontend/src/components/cohort_schema.rsAny excluded from string/number-array cohort filters

Note on UX

With this schema the Default Value is now a free-form JSON editor: values are entered as JSON (a string must be quoted, e.g. "default_str_ignore_this"). Unquoted text shows a not a valid JSON value hint; the backend validates against the full schema on submit.

Verification

  • cargo test -p frontend --lib — 6 new unit tests pass (schema resolution, Any→Monaco mapping, JSON parsing, invalid-type still errors)
  • cargo check -p frontend --lib on host and --target wasm32-unknown-unknown compile clean; cargo clippy clean
  • End-to-end in the running app: an anyOf key now renders an editable JSON value editor (no error), and creation persists via POST /default-config → 200

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for schema fields without a top-level type, including composed, referenced, and constant-value schemas.
    • These fields now use a JSON editor and accept arbitrary valid JSON values.
    • Added an “any” type badge to clarify the expected input format.
  • Bug Fixes

    • String and number array input options are no longer shown when they are incompatible with an unrestricted schema.
    • Invalid JSON values now display a clear validation message.

The single default-config key creation form (and the context, override, and
variant forms) could not create a key when the type schema had no top-level
`type` field — e.g. a JSON Schema combinator like `anyOf`/`oneOf`/`allOf`/
`$ref`, or a bare `const`. `SchemaType::try_from` errored with "type not
defined in schema", and the Default Value input was rendered disabled, so no
value could be entered and the key could not be saved.

The `SchemaType` is only used to pick which input widget to render; the value
itself is validated against the full JSON Schema on the backend. So a missing
top-level `type` should not be a hard failure.

Add a `SchemaType::Any` variant, returned by `try_from` when the schema object
has no `type`. It maps to a free-form Monaco/JSON editor and parses input as
arbitrary JSON. Genuinely malformed schemas (present-but-invalid `type`) still
error as before. This fixes every form that consumes `SchemaType` in one place;
the match arms are compiler-enforced.

Verified with unit tests (schema resolution, InputType mapping, JSON parsing)
and end-to-end in the running app: an `anyOf` key now renders an editable JSON
value editor and persists via POST /default-config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@knutties
knutties requested a review from a team as a code owner September 17, 2026 08:42
@semanticdiff-com

semanticdiff-com Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  crates/frontend/src/schema.rs  2% smaller
  crates/frontend/src/components/cohort_schema.rs  0% smaller
  crates/frontend/src/components/input.rs  0% smaller
  crates/frontend/src/components/override_form.rs  0% smaller

@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: a617a9df-f3ce-41ea-b980-1c1f8afdc7a3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 25c0b95b-4cb1-4973-9197-fd6db48b2b87

📥 Commits

Reviewing files that changed from the base of the PR and between 65ef921 and 15e018b.

📒 Files selected for processing (4)
  • crates/frontend/src/components/cohort_schema.rs
  • crates/frontend/src/components/input.rs
  • crates/frontend/src/components/override_form.rs
  • crates/frontend/src/schema.rs

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


Walkthrough

The frontend now represents schemas without a top-level type as SchemaType::Any. These schemas use JSON null by default, accept arbitrary JSON input through Monaco, display an any badge, and exclude array-specific input tabs.

Changes

Any schema support

Layer / File(s) Summary
Schema type resolution and defaults
crates/frontend/src/schema.rs
Adds SchemaType::Any, maps untyped composition, reference, and const schemas to it, returns JSON null by default, and adds coverage for valid and invalid schema types.
Any schema input handling
crates/frontend/src/components/input.rs
Maps SchemaType::Any to Monaco input and parses arbitrary JSON values. Unit tests cover strings, numbers, objects, and invalid JSON.
Any schema frontend rendering
crates/frontend/src/components/override_form.rs, crates/frontend/src/components/cohort_schema.rs
Displays an any type badge and explicitly excludes StringArray and NumberArray tabs for SchemaType::Any.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: ayushjain17, mahatoankitkumar

Merge Risk: ⚪ Minimal · up to 15e01

Untyped schemas now receive a JSON editor and valid JSON handling without an identified regression in the reviewed paths.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: support for type-less and composition-based JSON schemas in the key and configuration creation UI.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/anyof-schema-key-creation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit hops where schemas grow
Any JSON now has room to show
Monaco opens, clear and bright
Badges mark the type just right
Array tabs rest outside tonight

Comment @coderabbitai help to get the list of available commands.

Collapse the new `view!` block to a single line to satisfy `leptosfmt --check`
(run by `make check` in CI).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread crates/frontend/src/schema.rs Outdated
Comment on lines +86 to +88
/// Schema has no resolvable top-level `type` (e.g. `anyOf`/`oneOf`/`allOf`/`$ref`).
/// The value is accepted as free-form JSON and validated against the full schema
/// on the backend.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we please remove the inline comments in both the places

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — removed both the doc-comment on the Any variant and the comment in try_from (commit 968ac81).

Comment thread crates/frontend/src/schema.rs Outdated
SchemaType::Single(JsonSchemaType::Null) => {
Value::String(String::from("null"))
}
SchemaType::Any => Value::Null,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did we test this ?
I think this does not work correctly for some reason
that's why we use
Value::String(String::default()) in other places and Value::String(String::from("null")) for Null
so I think we should use either of these only over here as well

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on the default. It does render/edit correctly (verified in the browser — the Default Value became an editable Monaco editor showing null), and the other Monaco cases actually use real JSON values (Object -> {}, Array -> []), so Value::Null wasn't broken per se. But you're right that it's a poor default: null fails validation for basically every real anyOf/oneOf/const schema, so the user hits a validation error unless they edit it. Changed it to Value::String(String::default()) (commit 968ac81) — a valid editable default for the common anyOf: [string, ...] case and consistent with the Multiple convention. Skipped "null" since that seeds the literal string.

…r Any

Per review feedback on the SchemaType::Any support:
- Remove the explanatory comments on the `Any` variant and the `try_from`
  early-return.
- Default value for `Any` is now `Value::String(String::default())` instead of
  `Value::Null`. `null` fails validation for typical anyOf/oneOf/const schemas,
  so an empty string is a safer editable default and matches the `Multiple`
  ("unknown type") convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SchemaType::Single(JsonSchemaType::Null) => {
Value::String(String::from("null"))
}
SchemaType::Any => Value::String(String::default()),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any doesn't necessarily mean string is a valid type. For example, {"anyOf":[{"type":"number"},{"type":"boolean"}]} would resolve to Any, but "" would be an invalid default. Should Any avoid providing a default value altogether (e.g. Option) and let the Monaco input/user provide the value?

Not a blocker for this PR since backend schema validation still protects correctness. Good to merge; we can handle this as a follow-up.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants