Added JSON mapping plan implementation#14
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a JSON-based mapping plan implementation that allows entity types to be mapped from source to target names using configuration loaded from a JSON file. The implementation extends the existing mapping infrastructure by adding a new JsonMappingPlan class and introducing an IsValid property to the IMappingPlan interface.
Key changes:
- New
JsonMappingPlanclass that loads mappings from JSON files with case-insensitive matching - Added
IsValidproperty toIMappingPlaninterface to indicate mapping plan validity - Added Newtonsoft.Json dependency for JSON deserialization
Reviewed changes
Copilot reviewed 11 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| idou.Core/idou.Core.csproj | Added Newtonsoft.Json 13.0.4 package reference |
| idou.Core/Mapping/JsonMappingPlan.cs | Implements IMappingPlan to load and apply entity type mappings from JSON files |
| idou.Core/Mapping/JsonMapping.cs | Data model representing a single source-to-target mapping entry |
| idou.Core/Mapping/IMappingPlan.cs | Added IsValid property to the interface |
| idou.Core/Mapping/DefaultMappingPlan.cs | Implemented IsValid property (always returns true) |
| idou.Core.Tests/idou.Core.Tests.csproj | Configured test asset JSON files to be copied to output directory |
| idou.Core.Tests/Mapping/assets/valid.json | Test fixture with a sample mapping configuration |
| idou.Core.Tests/Mapping/assets/empty.json | Test fixture with empty mappings array |
| idou.Core.Tests/Mapping/JsonMappingPlanTests.cs | Unit tests for JsonMappingPlan functionality |
| idou.Core.Tests/Engine/ChangePipelineTests.cs | Updated RecordingMappingPlan to implement new IsValid property |
| docs/flow.puml | Added PlantUML diagram documenting sync job execution flow |
| .gitignore | Replaced minimal ignore rules with comprehensive .NET/IDE gitignore patterns |
| .idea/.idea.idou/.idea/vcs.xml | JetBrains Rider VCS configuration |
| .idea/.idea.idou/.idea/indexLayout.xml | JetBrains Rider index layout configuration |
| .idea/.idea.idou/.idea/.gitignore | JetBrains Rider ignore patterns |
Files not reviewed (3)
- .idea/.idea.idou/.idea/.gitignore: Language not supported
- .idea/.idea.idou/.idea/indexLayout.xml: Language not supported
- .idea/.idea.idou/.idea/vcs.xml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
idou.Core/Mapping/JsonMapping.cs
Outdated
| public string Source { get; set; } | ||
| public string Target { get; set; } |
There was a problem hiding this comment.
The Source and Target properties are nullable reference types but should not be null for valid mappings. Consider adding null-forgiving operators, making them required properties, or adding validation to ensure they're not null during deserialization. Currently, if the JSON contains null values for these properties, a NullReferenceException will occur when JsonMappingPlan tries to use them in line 19 (ToDictionary) or line 27 (new EntityType).
| public string Source { get; set; } | |
| public string Target { get; set; } | |
| public required string Source { get; set; } = null!; | |
| public required string Target { get; set; } = null!; |
| using idou.Core.Domain; | ||
| using idou.Core.Mapping; | ||
|
|
||
| namespace idou.Core.Tests.Mapping; | ||
|
|
||
| public class JsonMappingPlanTests | ||
| { | ||
| [Fact] | ||
| public void MappingPlanIsValid_WhenTheFileExistsAndHasMappings() | ||
| { | ||
| var plan = new JsonMappingPlan("Mapping/assets/valid.json"); | ||
| Assert.True(plan.IsValid); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Throws_WhenTheFileDoesNotExist() | ||
| { | ||
| Assert.Throws<FileNotFoundException>(() => new JsonMappingPlan("Mapping/assets/does-not-exist.json")); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void MappingPlanIsInvalid_WhenTheMappingsAreEmpty() | ||
| { | ||
| var plan = new JsonMappingPlan("Mapping/assets/empty.json"); | ||
| Assert.False(plan.IsValid); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void MapsTheEntityType_WhenTheMappingExists() | ||
| { | ||
| var plan = new JsonMappingPlan("Mapping/assets/valid.json"); | ||
|
|
||
| var sourceType = new EntityType("users"); | ||
| var mapped = plan.MapEntityType(sourceType); | ||
|
|
||
| Assert.True(plan.IsValid); | ||
| Assert.Equal("accounts", mapped.Name); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void DoesNotMapTheEntityType_WhenNoMappingExists() | ||
| { | ||
| var plan = new JsonMappingPlan("Mapping/assets/valid.json"); | ||
|
|
||
| var sourceType = new EntityType("unknown"); | ||
| var mapped = plan.MapEntityType(sourceType); | ||
|
|
||
| Assert.Equal(sourceType.Name, mapped.Name); | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
The MapRecord method lacks test coverage. While MapEntityType is tested, there are no tests verifying that MapRecord correctly creates a new EntityRecord with mapped type and key, or that it properly copies the Attributes dictionary. Consider adding a test that verifies MapRecord behavior, similar to the existing tests for MapEntityType.
| File.ReadAllText(filePath) | ||
| ) ?? throw new InvalidOperationException("Invalid mapping file"); | ||
|
|
||
| _bySource = mappings.ToDictionary(m => m.Source, StringComparer.OrdinalIgnoreCase); |
There was a problem hiding this comment.
If the JSON file contains duplicate source values (considering case-insensitive comparison due to StringComparer.OrdinalIgnoreCase), the ToDictionary call will throw an ArgumentException. Consider either validating for duplicates and providing a clearer error message, or handling this case explicitly. There's also no test coverage for this scenario.
| var mappings = JsonConvert.DeserializeObject<List<JsonMapping>>( | ||
| File.ReadAllText(filePath) | ||
| ) ?? throw new InvalidOperationException("Invalid mapping file"); |
There was a problem hiding this comment.
If the JSON file contains malformed JSON, JsonConvert.DeserializeObject will throw a JsonException that isn't caught or handled. Consider adding try-catch to provide a more user-friendly error message, or document that callers should expect JsonException in addition to FileNotFoundException and InvalidOperationException.
| var mappings = JsonConvert.DeserializeObject<List<JsonMapping>>( | |
| File.ReadAllText(filePath) | |
| ) ?? throw new InvalidOperationException("Invalid mapping file"); | |
| List<JsonMapping> mappings; | |
| try | |
| { | |
| mappings = JsonConvert.DeserializeObject<List<JsonMapping>>( | |
| File.ReadAllText(filePath) | |
| ) ?? throw new InvalidOperationException("Invalid mapping file"); | |
| } | |
| catch (JsonException ex) | |
| { | |
| throw new InvalidOperationException("Invalid mapping file", ex); | |
| } |
No description provided.