feat(schema): implement Schema Migration System (#519)#968
Closed
yuvrajangadsingh wants to merge 33 commits intozio:mainfrom
Closed
feat(schema): implement Schema Migration System (#519)#968yuvrajangadsingh wants to merge 33 commits intozio:mainfrom
yuvrajangadsingh wants to merge 33 commits intozio:mainfrom
Conversation
This implements the core Schema Migration System for ZIO Schema 2: - MigrationAction ADT with 14 action types (AddField, DropField, Rename, TransformValue, Mandate, Optionalize, Join, Split, ChangeType, RenameCase, TransformCase, TransformElements, TransformKeys, TransformValues) - DynamicMigration: pure, serializable migration operating on DynamicValue - Migration[A, B]: typed wrapper with schema conversion - MigrationBuilder: fluent API with macro-powered selectors - Scala 3 selector macros converting _.field syntax to DynamicOptic - Scala 2.13 selector macros for cross-version support - Comprehensive test suite (13 tests) Key features: - All migrations are fully serializable (no closures) - Path-based actions using DynamicOptic - Structural reverse support - Composition via ++ operator - Error handling with path information
a8236d7 to
8aed127
Compare
Implement full compile-time validation in the MigrationBuilder for Scala 3: - Add MigrationComplete type class that validates migration completeness - Add MigrationValidationMacros for extracting field names and validating - Add MigrationBuilderMacros for type-level field tracking - Update MigrationBuilder to use transparent inline methods with macros - Track handled source fields and provided target fields at type level - Auto-map fields with same name in source and target - Add .build method that requires MigrationComplete evidence - Keep .buildPartial for partial migrations without validation - Add 5 new tests for MigrationBuilder functionality The validation ensures all source fields are handled (renamed, dropped, transformed) or auto-mapped, and all target fields are provided (added, renamed to) or auto-mapped.
4c2f654 to
bf5cd81
Compare
059c2e5 to
7f046f3
Compare
- Add 89 new tests (107 total) for MigrationAction types and edge cases - Test all MigrationAction types: TransformValue, Mandate, Optionalize, ChangeType, RenameCase, TransformCase, TransformElements, TransformKeys, TransformValues, Join, Split - Test error handling and path navigation for all node types - Test fieldName/from exception branches for AddField, DropField, Rename - Test error propagation in nested transformations - Achieve 80.12% branch coverage (meeting 80% threshold)
7f046f3 to
5a8a47f
Compare
Implement serializable expression system for migrations: - Add MigrationExpr: pure, serializable expression type with: - Literal, FieldRef, StringConcat, Arithmetic operations - Type conversions (ToString, ToInt, ToLong, etc.) - Conditional expressions and comparisons - DSL operators for ergonomic usage - Add expression-based MigrationActions: - TransformValueExpr: transform using MigrationExpr - ChangeTypeExpr: type conversion using expressions - JoinExpr: join fields with combine expression - SplitExpr: split field with multiple expressions - Update MigrationBuilder (Scala 2 & 3): - transformFieldExpr(): expression-based field transforms - changeFieldTypeExpr(): expression-based type conversion - joinFields(): join with expressions - splitField(): split with expressions - Improve changeFieldType() implementation - Add 56 new tests for MigrationExpr and expression actions - Branch coverage: 80.57% (above 80% threshold)
Adds two tests from issue zio#519 success criteria: - associativity law: (m1 ++ m2) ++ m3 == m1 ++ (m2 ++ m3) - structural reverse law: m.reverse.reverse == m
Adds tests for MigrationExprOps DSL methods that were missing coverage: - *, /, -, <, <=, >, >= operators - toLong, toFloat, toDouble, toBigInt, toBigDecimal, toBoolean, asString conversions
Adds tests for: - DSL === and =!= operators - DSL toInt conversion - MigrationExpr.field helper - Convert ToString for various primitive types (Long, Double, Float, Boolean, BigInt, BigDecimal, Char)
Adds 16 more tests to improve branch coverage: - SplitExpr reverse with combineExpr - Arithmetic ops with Long, Double, Float - BigInt/BigDecimal arithmetic error handling - Convert ToInt/ToDouble/ToLong/ToFloat/ToBigInt/ToBigDecimal/ToBoolean - Convert ToString for Short and Byte
Adds 20 more conversion tests for ToInt/ToLong/ToDouble/ToFloat from various source types (Short, Byte, Float, Double, BigInt, BigDecimal) to achieve better branch coverage on Scala 3.3.x
Adds 12 more tests for conversion coverage: - ToBigInt from Short, Byte, Int - ToBigDecimal from Short, Byte, Long, BigInt, String - Error cases for unsupported conversions - ToLong from Int
Author
|
/claim #519 Ready for review! This implementation addresses all success criteria from issue #519 with:
cc @jdegoes |
12 tasks
Author
|
@copilot review |
…d JMH benchmark Split the 2853-line MigrationSpec.scala into 4 focused spec files matching the repo's existing pattern (e.g., Patch module): - DynamicMigrationSpec: core DynamicMigration, path navigation, edge cases - MigrationActionSpec: action laws, error handling, field extraction - MigrationBuilderSpec: typed Migration[A,B] and builder API - MigrationExprSpec: expression system, arithmetic, executor paths Added JMH benchmark for DynamicMigration operations: - DynamicMigrationBenchmark: addField, rename, compose, nested, sequence - DynamicMigrationBenchmarkSpec: validates benchmark output consistency All 285 migration tests pass. No test logic changed.
Add Nest and Unnest migration actions for grouping/ungrouping fields into sub-records. Useful for schema evolution like nesting street/city/zip into an address sub-record. New test suites: - Nest/Unnest operations (11 tests) - Multi-step migration scenarios (8 tests) - Laws (identity, associativity, reverse-of-reverse) - Deep path operations (4-level nesting, cross-type paths) - Enum migration scenarios (6 tests) - Reverse round-trip properties (5 tests) - Builder edge cases and composition (10 tests) Also added nestFields/unnestFields JMH benchmarks.
Author
|
Hey @jdegoes — noticed this got closed. Is there specific feedback or something you'd like changed? Happy to rework if needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the schema migration system from #519.
The main design choice here was creating a dedicated
MigrationExprtype rather than modifying the existingSchemaExpr. The reason:SchemaExprcontains function-based types (IsNumeric,Optic[_, _, _]) that aren't serializable, and I didn't want to break or complicate existing code to make them work. A separateMigrationExprkeeps everything self-contained — all changes are in the migration package, nothing else is touched.How it works
Two-layer architecture following the existing
Patch/DynamicPatchpattern:DynamicMigration— the untyped, serializable core. A vector ofMigrationActionvalues (pure ADT, no closures) that operates onDynamicValue. Supports composition (++), structural reverse, and path-based navigation viaDynamicOptic.Migration[A, B]— typed wrapper that pairs aDynamicMigrationwith source/target schemas. Handles the conversion between typed values andDynamicValue.MigrationBuilder— fluent API using macros to convert_.fieldselector syntax intoDynamicOpticat compile time (both Scala 2.13 and 3). In Scala 3, there's also aMigrationCompletetype class that gives you a compile error if your migration doesn't cover all fields.MigrationExpr— pure expression type for computed values: arithmetic, string concat, comparisons, type conversions. All serializable, no closures.Actions
20 action types covering records (
AddField,DropField,Rename,TransformValue,Mandate,Optionalize,Nest,Unnest), joins/splits (Join,Split,JoinExpr,SplitExpr), type conversions (ChangeType,ChangeTypeExpr), enums (RenameCase,TransformCase), and collections (TransformElements,TransformKeys,TransformValues). Plus expression-based variants (TransformValueExpr).Nested migrations work naturally —
TransformCase,TransformElements, etc. contain their ownVector[MigrationAction], so you can express hierarchical transformations.Quick example
Tests
335 tests across 4 spec files, organized the same way the repo does it for
Patch(separatePatchSpec,DynamicPatchSpec, etc.):DynamicMigrationSpec— core ops, path navigation, Nest/Unnest, multi-step, lawsMigrationActionSpec— action laws, error handling, enum scenarios, reverse round-tripsMigrationBuilderSpec— typedMigration[A, B], builder API, compositionMigrationExprSpec— expression evaluation, arithmetic, string ops, type conversionsAlso added
DynamicMigrationBenchmarkwith 8 JMH benchmarks (addField, rename, composed, nested, sequence transform, reverse, nest, unnest) plus a spec that validates benchmark output.Why 23 files
12 of the 23 files are platform-specific macro code for Scala 2/3 cross-compilation. The metaprogramming APIs are completely different between
scala.reflectandscala.quoted, so each platform needs its own implementations. This is the same pattern used throughout the repo —Patch,Schema,Opticall have separate platform-specific files per concern.The remaining 11 are: 5 shared source files (core logic), 4 test files, 2 benchmark files.
Closes #519