Skip to content

Feature/iron validation#3

Open
sake92 wants to merge 11 commits into
mainfrom
feature/iron-validation
Open

Feature/iron validation#3
sake92 wants to merge 11 commits into
mainfrom
feature/iron-validation

Conversation

@sake92
Copy link
Copy Markdown
Owner

@sake92 sake92 commented Jun 5, 2026

Summary by CodeRabbit

  • New Features

    • Added validation framework supporting Iron, Validson, and None backends for schema validation
    • Integrated validation into code generation pipeline to produce refined, validated model types
    • New CLI parameter enables validation strategy selection during code generation
    • Iron validation generates constraint-based Scala type definitions
  • Tests

    • Added test coverage for Circe and Iron validation integration

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 53f4a5d6-d85f-454f-92a0-8553b408c0a2

📥 Commits

Reviewing files that changed from the base of the PR and between 445ecb0 and d48f6dc.

📒 Files selected for processing (1)
  • openapi4s/src/test/scala/ba/sake/openapi4s/OpenApiGeneratorSuite.scala
🚧 Files skipped from review as they are similar to previous changes (1)
  • openapi4s/src/test/scala/ba/sake/openapi4s/OpenApiGeneratorSuite.scala

📝 Walkthrough

Walkthrough

This PR introduces a pluggable validation backend system enabling optional Iron constraint generation for OpenAPI code generation. It adds three backend implementations, integrates validation into the code generation pipeline, threads validation type mappings through model generators, and wires CLI parameters and integration tests.

Changes

Iron Validation Backend Feature

Layer / File(s) Summary
Validation Backend Contract & Noop Implementation
openapi4s/src/main/scala/ba/sake/openapi4s/ValidationBackend.scala, openapi4s/src/main/scala/ba/sake/openapi4s/validation/NoneValidationBackend.scala
Defines sealed ValidationBackendId with None, Iron, and Validson variants and parsing; introduces ValidationBackend trait with generate method returning sources and type map; NoneValidationBackend implements the contract returning empty outputs while supporting all model backends.
Iron Constraint Generation
openapi4s/src/main/scala/ba/sake/openapi4s/validation/IronValidationBackend.scala
IronValidationBackend performs two-pass extraction of constrained properties from OpenAPI schemas (email, password, string patterns/lengths, numeric ranges, optional wrapping), resolves naming conflicts by grouping on property names, generates models/Newtypes.scala using scala.meta with Iron RefinedType definitions, and returns a schema/property-name-keyed type map for downstream generators.
Validson Backend Placeholder
openapi4s/src/main/scala/ba/sake/openapi4s/validation/ValidsonValidationBackend.scala
ValidsonValidationBackend declares Validson identity and Tupson support while returning empty outputs, reflecting that Validson validation is integrated directly into TupsonModelGenerator.
OpenApiWriter Validation Orchestration
openapi4s/src/main/scala/ba/sake/openapi4s/OpenApiWriter.scala
OpenApiWriter now accepts a validationBackend parameter, reads config.validation in the factory to resolve the backend, validates that the backend supports the selected model backend (throwing on incompatibility), invokes backend generation to obtain validationSources and validationTypeMap, and composes sources as validationSources ++ modelSources ++ frameworkSources. The Config case class gains a validation: String = "none" field.
ModelBackend & CirceModelGenerator Integration
openapi4s/src/main/scala/ba/sake/openapi4s/ModelBackend.scala, openapi4s/src/main/scala/ba/sake/openapi4s/circe/CirceModelGenerator.scala
ModelBackend.generator method gains an optional validationTypeMap parameter (defaulting to Map.empty) and forwards it to backend implementations; none, circe, and tupson backends pass the map to their respective generators. CirceModelGenerator constructor accepts the map and conditionally adds Iron Circe given import when the map is non-empty; during object schema generation, property types are resolved by consulting the map before falling back to SchemaUtils.resolveType, with optional properties wrapped in Option[...].
CLI Wiring & Integration Testing
cli/src/main/scala/ba/sake/openapi4s/cli/OpenApi4sMain.scala, openapi4s/src/test/scala/ba/sake/openapi4s/OpenApiGeneratorSuite.scala
CLI run method gains --validation parameter (defaulting to "none") and passes it to OpenApiWriter.Config. Integration test for circe + iron validation generates sources into a temp folder, asserts models/Newtypes.scala exists along with model files, verifies Iron and constraint imports in Newtypes.scala, and confirms Iron Circe given import in Pet.scala. Test infrastructure adds a printGeneratedSources helper used across four existing tests and the new validation test.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A validation backend takes the stage,
Iron constraints refined on the page,
Type maps flow through the generator's might,
Newtypes emerge with syntax so tight! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feature/iron validation' is partially related to the changeset. It refers to a real and significant part of the change (Iron validation backend implementation), but uses a branch-naming convention rather than a clear summary of the main change, and doesn't clearly indicate this is about adding validation support broadly to the code generation system. Consider using a more descriptive title like 'Add validation backend support with Iron integration' to better communicate the primary change to the codebase.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/iron-validation

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

Copy link
Copy Markdown

@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.

Actionable comments posted: 5

🧹 Nitpick comments (1)
docs/superpowers/plans/2026-06-05-iron-validation.md (1)

156-157: ⚡ Quick win

Clarify the expected compile result in Task 2 to avoid contradictory guidance.

“compile succeeds” conflicts with “backends not yet created … pending compilation errors.” Please make this expectation deterministic (either require temporary stubs first, or expect failure until Task 3/4).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-06-05-iron-validation.md` around lines 156 - 157,
Clarify Task 2's expected compile outcome by either (A) stating that compilation
should succeed and instructing the author to add minimal temporary stubs for
IronValidationBackend and ValidsonValidationBackend so the build passes, or (B)
stating that compilation is expected to fail until Task 3 and Task 4 add the
real backends; update the text to explicitly choose one of these two options and
reference the backend symbols IronValidationBackend and
ValidsonValidationBackend and the follow-up tasks (Task 3/Task 4) so readers
know whether they must add stubs in Task 2 or await later tasks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-06-05-iron-validation.md`:
- Around line 16-31: The Markdown file contains two adjacent tables that violate
markdownlint rule MD058; edit
docs/superpowers/plans/2026-06-05-iron-validation.md and insert a single blank
line before and after each table block (i.e., add an empty line separating the
preceding paragraph and the following content paragraph from each table) so both
tables are surrounded by blank lines and the MD058 warning is resolved.
- Around line 329-351: The two buildNumericConstraintKey methods overload only
by Option[Long] vs Option[Double], which erases to the same JVM signature;
rename one (e.g., buildNumericConstraintKeyLong and
buildNumericConstraintKeyDouble) or make a single generic method (e.g.,
buildNumericConstraintKey[T](prefix:String, minimum: Option[T], maximum:
Option[T])) and update all call sites (notably where Num32/Num64 construct keys)
to call the new names or the generic form so compilation no longer fails due to
signature erasure.

In
`@openapi4s/src/main/scala/ba/sake/openapi4s/validation/IronValidationBackend.scala`:
- Around line 105-106: The Password branch in the pattern matching (case pwd:
SchemaDefinition.Password) always returns Some(...) even when pwd.minLength,
pwd.maxLength and pwd.pattern are absent, which later causes a reduceLeft on an
empty constraints list to crash; change the branch to return None when there are
no actual constraints (minLength/maxLength are empty and pattern is empty/None)
and only return Some(buildConstraintKey(...)) when at least one constraint is
present; apply the same presence-check approach to the other
constraint-producing branches that later feed the reduceLeft so they also return
None when they produce no constraints.
- Around line 140-141: The code currently concatenates raw regexes with "|" via
pattern.foreach(p => parts += s"Match=$p") and parts.result().mkString("|"),
then later splits on "|" which corrupts alternation-containing patterns; fix by
encoding each pattern before joining (e.g. Base64 or URL-encode each p when
building the parts) and decode when parsing (replace the split-by-"\\|" logic
with decoding of each encoded segment), or alternatively switch to a delimiter
guaranteed not to appear in regexes (and update both the builder and the parser
to use that delimiter). Ensure the change updates both the construction site
(pattern.foreach / parts.result().mkString) and the parsing site that currently
splits on "|" so Match constraints with alternation remain intact.
- Around line 197-209: The code in IronValidationBackend.scala that builds Range
constraints (the Type.Apply(Type.Name("GreaterEqual")/ "LessEqual") branches)
incorrectly converts numeric bounds using value.toDouble.toInt, which truncates
Int64/Long limits; change the conversion to respect baseType: when baseType ==
"Int" use value.toInt and emit Lit.Int(...), when baseType == "Long" use
value.toLong and emit Lit.Long(...), and keep Float/Double using value.toDouble
with Lit.Double; update both the "Min=" and "Max=" branches (the
Type.Apply(Type.Name("GreaterEqual") and Type.Apply(Type.Name("LessEqual"))
sites) accordingly so Long bounds are preserved.

---

Nitpick comments:
In `@docs/superpowers/plans/2026-06-05-iron-validation.md`:
- Around line 156-157: Clarify Task 2's expected compile outcome by either (A)
stating that compilation should succeed and instructing the author to add
minimal temporary stubs for IronValidationBackend and ValidsonValidationBackend
so the build passes, or (B) stating that compilation is expected to fail until
Task 3 and Task 4 add the real backends; update the text to explicitly choose
one of these two options and reference the backend symbols IronValidationBackend
and ValidsonValidationBackend and the follow-up tasks (Task 3/Task 4) so readers
know whether they must add stubs in Task 2 or await later tasks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c70b2963-6e6a-4807-93c6-b9c98ce1d7cb

📥 Commits

Reviewing files that changed from the base of the PR and between e218155 and ba5a2f4.

📒 Files selected for processing (11)
  • .gitignore
  • cli/src/main/scala/ba/sake/openapi4s/cli/OpenApi4sMain.scala
  • docs/superpowers/plans/2026-06-05-iron-validation.md
  • openapi4s/src/main/scala/ba/sake/openapi4s/ModelBackend.scala
  • openapi4s/src/main/scala/ba/sake/openapi4s/OpenApiWriter.scala
  • openapi4s/src/main/scala/ba/sake/openapi4s/ValidationBackend.scala
  • openapi4s/src/main/scala/ba/sake/openapi4s/circe/CirceModelGenerator.scala
  • openapi4s/src/main/scala/ba/sake/openapi4s/validation/IronValidationBackend.scala
  • openapi4s/src/main/scala/ba/sake/openapi4s/validation/NoneValidationBackend.scala
  • openapi4s/src/main/scala/ba/sake/openapi4s/validation/ValidsonValidationBackend.scala
  • openapi4s/src/test/scala/ba/sake/openapi4s/OpenApiGeneratorSuite.scala

Comment on lines +16 to +31
| # | File | Purpose |
|---|------|---------|
| C1 | `openapi4s/src/main/scala/ba/sake/openapi4s/ValidationBackend.scala` | Trait + ValidationBackendId sealed hierarchy |
| C2 | `openapi4s/src/main/scala/ba/sake/openapi4s/validation/NoneValidationBackend.scala` | No-op default |
| C3 | `openapi4s/src/main/scala/ba/sake/openapi4s/validation/IronValidationBackend.scala` | 2-pass collect, dedup, generate Newtypes.scala + type map |
| C4 | `openapi4s/src/main/scala/ba/sake/openapi4s/validation/ValidsonValidationBackend.scala` | Thin wrapper delegating to ValidsonUtils |

### Modify
| # | File | Change |
|---|------|--------|
| M1 | `openapi4s/src/main/scala/ba/sake/openapi4s/OpenApiWriter.scala` | Add `validation` to Config, wire ValidationBackend, call its generate |
| M2 | `openapi4s/src/main/scala/ba/sake/openapi4s/ModelBackend.scala` | Pass validation info to circe generator factory |
| M3 | `openapi4s/src/main/scala/ba/sake/openapi4s/circe/CirceModelGenerator.scala` | Accept optional `newtypeMap`, swap types, add iron.circe.given import |
| M4 | `cli/src/main/scala/ba/sake/openapi4s/cli/OpenApi4sMain.scala` | Add `--validation` arg |
| M5 | `openapi4s/src/test/scala/ba/sake/openapi4s/OpenApiGeneratorSuite.scala` | Add integration test for `--models circe --validation iron` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add blank lines around the two tables to satisfy markdownlint MD058.

This keeps docs CI/lint clean and avoids noisy warnings.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 16-16: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)


[warning] 24-24: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-06-05-iron-validation.md` around lines 16 - 31,
The Markdown file contains two adjacent tables that violate markdownlint rule
MD058; edit docs/superpowers/plans/2026-06-05-iron-validation.md and insert a
single blank line before and after each table block (i.e., add an empty line
separating the preceding paragraph and the following content paragraph from each
table) so both tables are surrounded by blank lines and the MD058 warning is
resolved.

Comment thread docs/superpowers/plans/2026-06-05-iron-validation.md Outdated
Comment thread openapi4s/src/main/scala/ba/sake/openapi4s/validation/IronValidationBackend.scala Outdated
Comment thread openapi4s/src/main/scala/ba/sake/openapi4s/validation/IronValidationBackend.scala Outdated
Comment thread openapi4s/src/main/scala/ba/sake/openapi4s/validation/IronValidationBackend.scala Outdated
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