Skip to content

feat: wire --json output to rich Pydantic models (STOPS-7492)#909

Merged
vjeeva merged 4 commits intomainfrom
feat/json-enrichment
Mar 31, 2026
Merged

feat: wire --json output to rich Pydantic models (STOPS-7492)#909
vjeeva merged 4 commits intomainfrom
feat/json-enrichment

Conversation

@vjeeva
Copy link
Copy Markdown
Contributor

@vjeeva vjeeva commented Mar 31, 2026

Summary

All 9 commands with rich Pydantic model subclasses now produce their full structured output when invoked with --json. No command is left using the generic CommandResult.detail dict when a dedicated model exists.

Commands that already returned dicts (no implementation changes)

Command Model
check-connectivity ConnectivityCheckResult
connections ConnectionsResult
status StatusResult
precheck PrecheckResult
diff-schemas DiffSchemasResult (new model)

Commands that previously returned None (implementation changes)

Command Model What changed
sync-sequences SyncSequencesResult _sync_sequences now returns per-sequence detail: PK vs non-PK classification, sync method, source/destination values, stride application, skip reasons
sync-tables SyncTablesResult New dump_and_load_tables_with_details utility tracks per-table load/skip/error with duration_ms
validate-data ValidateDataResult Each validation strategy (random_100, latest_100, no_pkey_presence) is wrapped to capture pass/fail
create-indexes CreateIndexesResult New create_target_indexes_with_details utility tracks per-index create/skip/fail with duration_ms

New model: DiffSchemasResult

diff-schemas already returned a dict but had no dedicated model. New DiffSchemasResult with DiffSchemaRow provides per-DB result (match/mismatch/skipped), the unified diff text on mismatch (previously only logged), and a computed all_match field.

Remaining commands (no rich model -- by design)

Setup, teardown variants, analyze, load-constraints, login commands, etc. use the generic CommandResult per the architectural rule: they're simple pass/fail without per-item structured output.

Files changed

File Change
pgbelt/cmd/helpers.py 9 builder functions + _RICH_MODEL_BUILDERS mapping for all rich commands
pgbelt/cmd/sync.py sync_sequences, sync_tables, validate_data now return structured dicts
pgbelt/cmd/schema.py create_indexes now returns structured dict
pgbelt/util/dump.py New dump_and_load_tables_with_details, create_target_indexes_with_details; validate_schema_dump now includes diff text
pgbelt/models/schema.py New DiffSchemaRow, DiffSchemasResult
pgbelt/models/__init__.py Export new models
tests/pgbelt/cmd/test_json_flag.py Tests for all 9 rich model builders
tests/pgbelt/models/test_round_trips.py Round-trip tests for DiffSchemasResult

Test plan

  • All 55 unit tests pass (pytest tests/ --ignore=tests/integration)
  • Pre-commit hooks pass (black, flake8, ruff)
  • CI passes
  • Verify all 9 --json commands produce their rich model output

vjeeva added 2 commits March 31, 2026 14:57
…mands (STOPS-7492)

The 4 commands that already return structured dicts (check-connectivity,
connections, status, precheck) now produce their full Pydantic model
output when invoked with --json, instead of dumping raw dicts into the
generic CommandResult.detail field.

The conversion is centralized in _build_json_output via a command-name
to builder-function mapping, so no command implementations were modified.
Simple pass/fail commands continue to use the generic CommandResult.

Made-with: Cursor
… to rich models

Commands that previously returned None now return structured dicts which
are converted into their full Pydantic models (SyncSequencesResult,
SyncTablesResult, ValidateDataResult, CreateIndexesResult) when --json
is used.

sync-sequences: returns per-sequence detail (PK vs non-PK, method,
  source/destination values, stride application, skip reasons)
sync-tables: returns per-table load/skip detail with duration_ms
  via new dump_and_load_tables_with_details utility
validate-data: wraps each validation strategy to capture pass/fail
create-indexes: returns per-index create/skip/fail detail with
  duration_ms via new create_target_indexes_with_details utility

Non-JSON mode behavior is unchanged -- the dicts are ignored by
run_with_configs when json_mode is False.

Made-with: Cursor
@autodesk-chorus
Copy link
Copy Markdown

Chorus detected one or more security issues with this pull request. See the Checks tab for more details.

As a reminder, please follow the secure code review process as part of the Secure Coding Non-Negotiable requirement.

vjeeva added 2 commits March 31, 2026 15:16
New DiffSchemasResult model with per-DB DiffSchemaRow (match/mismatch/
skipped) and computed all_match field. On mismatch the unified diff text
is now included in the returned dict (previously only logged).

This completes the wiring of all 9 commands that have rich models.

Made-with: Cursor
pg_isready defaults to the OS user (root) which doesn't exist as a
Postgres role, causing intermittent healthcheck failures.

Made-with: Cursor
@vjeeva vjeeva merged commit bcb5f64 into main Mar 31, 2026
7 checks passed
@vjeeva vjeeva deleted the feat/json-enrichment branch March 31, 2026 19:52
@github-actions github-actions bot locked and limited conversation to collaborators Mar 31, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant