feat(core): dynamic path A2 — config model, JSON parser and compiler - #59
Merged
Conversation
Second step of Epic A (D21). Reports can now be expressed as data and compiled into the same runnable report the fluent path produces — no parallel pipeline. - Abstractions: serializer-agnostic config DTOs (ReportConfig, SourceConfig, ColumnConfig, OutputConfig, DestinationConfig), the IReportConfigParser contract, and IConfigSourceProvider (the dynamic equivalent of a typed source factory). Additive, SemVer-minor (D25); the DTOs carry no JSON coupling. - Core: JsonReportConfigParser (System.Text.Json; case-insensitive, string enums, property-bag values converted to CLR primitives / ISO DateTime like JobParameters) and ReportConfigCompiler, which builds a CompiledReport over the positional ReportRecord. Columns become Positional(...) getters; source/format/destination are resolved from DI by stable id (IConfigSourceProvider / IWriterFactory.Format / IDestinationFactory.Type), all resolved up front so a missing registration fails fast before the source is built. - Filter is parsed but compilation is deferred to A4 (the compiler rejects it explicitly). Tests (+7, 33 green Core): full parse with primitive coercion; empty/malformed rejection; config compiled and run end-to-end through ReportRunner; filter and missing-factory both surface a ConfigurationException.
Sonar new-code coverage was 70.6% (< 80%): the converter's Write path and several Read branches were unexercised. The config parser is read-only by design (IReportConfigParser exposes only Parse), so Write now throws NotSupportedException instead of carrying speculative serialization code, and the parse test exercises every Read value kind (string, long, double, bool, null, ISO DateTime, nested element).
Adds sample 04-dynamic-config-csv demonstrating A2: a report defined entirely in report.json (no typed POCO) is parsed, compiled and run, writing a real CSV. The JSON drives the report name, source selection, columns/schema and output/destination selection; an in-memory IConfigSourceProvider stands in until the SQL config source (A3), and the CSV/Local factories are pre-wired in DI until config option binding (A5). Registered in the solution under the samples folder.
Owner
Author
|
Added sample |
- Provide DateTimeKind in the sample's seed data (S6562). - Reword the sample header comment so it is not flagged as commented-out code (S125). - Use explicit types where the type is not apparent, per the repo .editorconfig (csharp_style_var_elsewhere = false) — clears the IDE0008 suggestions on new code. - Use the concrete JsonReportConfigParser for the test field (CA1859). No behavior change; 33 green Core tests, sample still runs.
|
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.



Second step of Epic A — dynamic path (D21), fully additive (D25). Builds on A1 (#58): reports can now be expressed as data and compiled into the same runnable report the fluent path produces.
Changes
Abstractions — serializer-agnostic config model (no JSON coupling):
ReportConfig,SourceConfig,ColumnConfig,OutputConfig,DestinationConfigIReportConfigParser— parses a document into aReportConfigIConfigSourceProvider— the dynamic equivalent of a typed source factory: builds anIBatchSource<ReportRecord>from aSourceConfig+ the report schemaCore:
JsonReportConfigParser(System.Text.Json) — case-insensitive names, string enums, comments/trailing commas, and property-bag values coerced to CLR primitives / ISODateTime(same convention asJobParameters).ReportConfigCompiler— turns aReportConfiginto aCompiledReportover the positionalReportRecord: columns →Positional(...)getters; source/format/destination resolved from DI by stable id (IConfigSourceProvider.Type,IWriterFactory.Format,IDestinationFactory.Type). All registrations are resolved up front so a missing provider/factory fails fast before the source is built.The filter is parsed into the config but its compilation is deferred to A4 — the compiler rejects a config that declares one, with a clear message (no silent drop).
Tests (+7, 33 green Core)
"limit": 10→long,"type": "Integer"→ enum, omittednullable→true).ConfigurationException.ReportRunner→ rows reach the destination; the provider receives the parsed source section.ConfigurationException(deferred to A4).xlsx) →ConfigurationException.Acceptance
A2 in
PLAN.md: golden config → compiled, runnable report. ✅Next
A3 (SQL source from config — real
IConfigSourceProviderfor"sql"), A4 (JsonLogic filter), A5 (DI sugar + dynamic trigger endpoint).