Skip to content

feat(wren): add context init --from-mdl to import MDL JSON as YAML project#1516

Merged
goldmedal merged 1 commit intofeat/cli-0.2.0from
feat/init-from-mdl
Apr 4, 2026
Merged

feat(wren): add context init --from-mdl to import MDL JSON as YAML project#1516
goldmedal merged 1 commit intofeat/cli-0.2.0from
feat/init-from-mdl

Conversation

@goldmedal
Copy link
Copy Markdown
Contributor

@goldmedal goldmedal commented Apr 4, 2026

Summary

  • Extends wren context init with --from-mdl <path> to convert a legacy camelCase MDL JSON into a v2 YAML project in one command
  • Adds --force flag to allow overwriting an existing project
  • Pure file-conversion — no database connection required, consistent with context build

Changes

wren/src/wren/context.py

  • _camel_to_snake() — known mapping table + generic regex fallback
  • _convert_keys_to_snake() — recursive key conversion (inverse of _convert_keys)
  • ProjectFile dataclass — relative_path + content
  • convert_mdl_to_project() — MDL JSON → list of ProjectFile (pure function, testable)
  • write_project_files() — writes files to disk with --force guard

wren/src/wren/context_cli.py

  • init extended: --from-mdl (optional path) and --force (bool)
  • Without --from-mdl: existing scaffold behavior unchanged
  • With --from-mdl: loads JSON, converts, writes, prints next-steps hint

wren/tests/unit/test_convert_mdl.py (new, 25 tests)

  • _camel_to_snake parametric tests + round-trip guarantee for all known keys
  • Full integration test (models, views, relationships, instructions)
  • write_project_files guard tests (refuse overwrite, force overwrite)
  • convert → build round-trip test
  • Edge cases: empty MDL, missing dataSource, unknown camelCase keys, empty relationships
  • CLI tests via typer.testing.CliRunner

Test plan

  • uv run pytest tests/unit/test_convert_mdl.py — 25/25 passed
  • just lint — clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Import MDL JSON files into Wren projects using wren context init --from-mdl
    • Generate complete project structure with YAML configurations and SQL files from MDL source
    • Use --force flag to overwrite existing project configurations during initialization

…oject

Adds --from-mdl flag to `wren context init` so users with legacy mdl.json
files can migrate to the v2 YAML project workflow in one command.

- context.py: _camel_to_snake, _convert_keys_to_snake, ProjectFile,
  convert_mdl_to_project, write_project_files
- context_cli.py: extend init with --from-mdl and --force flags
- tests/unit/test_convert_mdl.py: 25 tests (unit, integration, round-trip, CLI, edge cases)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 4, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08eeaf7f-2a7d-44a0-b551-6d8c7721c3a5

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:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/init-from-mdl

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

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
wren/src/wren/context_cli.py (1)

59-65: Add error handling for invalid JSON.

If the MDL file contains malformed JSON, json.loads raises JSONDecodeError with a raw traceback. Wrapping this in a try-except would provide a cleaner user experience.

🛡️ Proposed fix to handle JSON parse errors
-        mdl_json = json.loads(mdl_path.read_text())
+        try:
+            mdl_json = json.loads(mdl_path.read_text())
+        except json.JSONDecodeError as e:
+            typer.echo(f"Error: Invalid JSON in {mdl_path}: {e}", err=True)
+            raise typer.Exit(1)
         files = convert_mdl_to_project(mdl_json)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wren/src/wren/context_cli.py` around lines 59 - 65, Wrap the JSON parsing
call so malformed MDL yields a friendly error: surround the
json.loads(mdl_path.read_text()) with a try/except that catches
json.JSONDecodeError, call typer.echo with a clear message including the
mdl_path (and optionally the JSON error message) to stderr, and then raise
typer.Exit(1) instead of letting the raw traceback propagate; keep the
subsequent convert_mdl_to_project and write_project_files(project_path,
force=force) flow unchanged so only parsing errors are handled here.
wren/src/wren/context.py (1)

112-114: Consider defensive handling for missing model name.

If a model in the input MDL lacks a name field, line 114 raises an unhandled KeyError. Since this is a user-facing import tool, a clearer error message would improve UX.

💡 Optional: Add validation for missing name
     for model in mdl_json.get("models", []):
         model_snake = _convert_keys_to_snake(model)
-        name = model_snake["name"]
+        name = model_snake.get("name")
+        if not name:
+            raise ValueError(f"Model missing required 'name' field: {model_snake}")
         dir_path = f"models/{name}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wren/src/wren/context.py` around lines 112 - 114, The loop over
mdl_json.get("models", []) accesses model_snake["name"] directly which raises
KeyError for missing model names; in the block that iterates models (where
mdl_json, _convert_keys_to_snake and model_snake are used) add a defensive check
for the "name" key (or use model_snake.get("name")) and if missing raise a
clear, user-facing error (e.g., ValueError) or log a descriptive message
including the model payload or index and skip processing, so import failures
surface with an actionable message rather than a raw KeyError.
wren/tests/unit/test_convert_mdl.py (1)

309-323: Consider extracting CliRunner setup to a fixture.

The CliRunner import and instantiation is repeated in each CLI test. A shared fixture would reduce duplication.

♻️ Optional: Extract runner fixture

Add a fixture at the module level:

`@pytest.fixture`
def runner():
    from typer.testing import CliRunner
    return CliRunner()

Then use it in tests:

-def test_cli_init_from_mdl(tmp_path: Path, sample_mdl_file: Path):
-    from typer.testing import CliRunner
-
-    from wren.cli import app
-
-    runner = CliRunner()
+def test_cli_init_from_mdl(tmp_path: Path, sample_mdl_file: Path, runner):
+    from wren.cli import app
+
     project_dir = tmp_path / "project"
     result = runner.invoke(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wren/tests/unit/test_convert_mdl.py` around lines 309 - 323, Extract the
repeated CliRunner setup into a pytest fixture and update tests to use it:
create a module-level fixture named e.g. runner that imports and returns
typer.testing.CliRunner(), then update test functions like
test_cli_init_from_mdl to accept the runner fixture instead of creating a new
CliRunner instance inside the test; ensure references in the test use the
injected runner when invoking app (from wren.cli import app) and remove the
in-test import/instantiation to eliminate duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@wren/src/wren/context_cli.py`:
- Around line 59-65: Wrap the JSON parsing call so malformed MDL yields a
friendly error: surround the json.loads(mdl_path.read_text()) with a try/except
that catches json.JSONDecodeError, call typer.echo with a clear message
including the mdl_path (and optionally the JSON error message) to stderr, and
then raise typer.Exit(1) instead of letting the raw traceback propagate; keep
the subsequent convert_mdl_to_project and write_project_files(project_path,
force=force) flow unchanged so only parsing errors are handled here.

In `@wren/src/wren/context.py`:
- Around line 112-114: The loop over mdl_json.get("models", []) accesses
model_snake["name"] directly which raises KeyError for missing model names; in
the block that iterates models (where mdl_json, _convert_keys_to_snake and
model_snake are used) add a defensive check for the "name" key (or use
model_snake.get("name")) and if missing raise a clear, user-facing error (e.g.,
ValueError) or log a descriptive message including the model payload or index
and skip processing, so import failures surface with an actionable message
rather than a raw KeyError.

In `@wren/tests/unit/test_convert_mdl.py`:
- Around line 309-323: Extract the repeated CliRunner setup into a pytest
fixture and update tests to use it: create a module-level fixture named e.g.
runner that imports and returns typer.testing.CliRunner(), then update test
functions like test_cli_init_from_mdl to accept the runner fixture instead of
creating a new CliRunner instance inside the test; ensure references in the test
use the injected runner when invoking app (from wren.cli import app) and remove
the in-test import/instantiation to eliminate duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f366aa19-5fc6-4e45-aff0-fb26d1d3304a

📥 Commits

Reviewing files that changed from the base of the PR and between 6a8ea38 and 4dc3b27.

⛔ Files ignored due to path filters (1)
  • wren/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • wren/src/wren/context.py
  • wren/src/wren/context_cli.py
  • wren/tests/unit/test_convert_mdl.py

@goldmedal goldmedal merged commit e22342d into feat/cli-0.2.0 Apr 4, 2026
10 checks passed
@goldmedal goldmedal deleted the feat/init-from-mdl branch April 4, 2026 14:20
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.

1 participant