Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Path normalization via new NodeTransformer interface #413

Merged
merged 4 commits into from
Jul 15, 2024

Conversation

rocketraman
Copy link
Contributor

@rocketraman rocketraman commented Mar 29, 2024

Add a generic node transformer interface NodeTransformer, which can be used to transform nodes in arbitrary ways at configuration load time.

Add an implementation of this called PathNormalizer.

The purpose of the path normalizer is to normalize all inbound paths by making them lower case, and removing any "-" and "_" characters. Normalizing paths means sources with different idiomatic approaches to defining key values will all map correctly to the defined config classes. It makes it possible to implement #410.

The only downside to this is multiple config attributes in the same class that differ only by case can no longer be disambiguated. This should be a rare case and the advantages are more than worth losing this "feature". Therefore, in my opinion this should be installed by default, but I have not done that in this PR until I hear some feedback from @sksamuel. The feature is thus installed by default, and the now unnecessary parameter mappers removed by default.

We also add a LowercaseParameterMapper by default which can handle the normalized paths.

This PR is stacked on top of #412 and will be rebased and moved out of draft once that PR is merged.

@sksamuel
Copy link
Owner

I think this might be a better approach in general than the parameter mappers that are applied at runtime.
Perhaps (for 3.0 which can be the next release) we decide that paths get normalized while they are being cascaded, with sensible, spring style rules. Things like snake case mappings could be applied by default, and optionally disabled.
This would solve #395

It might also make env support easier as we can just layer the whole set of env vars over the final node (if we decide that THIS_FORMAT_IS_THE_CANONICAL_FORMAT)

@rocketraman
Copy link
Contributor Author

rocketraman commented Apr 1, 2024

I think this might be a better approach in general than the parameter mappers that are applied at runtime. Perhaps (for 3.0 which can be the next release) we decide that paths get normalized while they are being cascaded, with sensible, spring style rules. Things like snake case mappings could be applied by default, and optionally disabled. This would solve #395

It looks like this PR solves #395 already, simply with the addition of the PathNormalizer, which I recommend we should do by default. I've added a unit test for that case.

And yes, I believe the PathNormalizer could replace all the case implementations of the ParameterMapper. We would keep the parameter mappers that provide annotation support (Alias, Json, Serial), but I think always lower-case the output of them to match the PathNormalizer.

If you want me to make those changes in this PR I can, or we can do it as a follow-up. UPDATE: I did it in this PR.

It might also make env support easier as we can just layer the whole set of env vars over the final node (if we decide that THIS_FORMAT_IS_THE_CANONICAL_FORMAT)

Yeah, my next PR (#414) builds on this to address the env var stuff. I tried to implement it in a way that maintains backward compatibility. If you had a different idea in mind with "layer the whole set of env vars over the final node" can you explain further? #368 is also relevant I think.

@rocketraman rocketraman marked this pull request as ready for review April 1, 2024 14:52
@rocketraman rocketraman marked this pull request as draft April 1, 2024 14:57
@sksamuel
Copy link
Owner

sksamuel commented Apr 3, 2024

It might also make env support easier as we can just layer the whole set of env vars over the final node (if we decide that THIS_FORMAT_IS_THE_CANONICAL_FORMAT)

Yeah, my next PR (#414) builds on this to address the env var stuff. I tried to implement it in a way that maintains backward compatibility. If you had a different idea in mind with "layer the whole set of env vars over the final node" can you explain further? #368 is also relevant I think.

I just mean that we can take the System.envvar map and make it an always registered property source that is applied last.

@rocketraman rocketraman marked this pull request as ready for review April 4, 2024 03:41
The purpose of the path normalizer is to normalize all inbound paths
by making them lower case, and removing any "-" characters. Normalizing
paths means sources with different idiomatic approaches to defining
key values will all map correctly to the defined config classes.

The only downside to this is multiple config attributes in the same
class that differ only by case can no longer be disambiguated. This
should be a rare case and the advantages are more than worth losing
this "feature".

We also add a LowercaseParameterMapper by default which can handle
the normalized paths.
With path normalization (and even before path normalization), it was
possible for the reported path to not match the input value. For
example, with environment variables, the environment variable may have
been `FOO__BAR`, but the report showed `FOO.BAR`, which makes it harder
for users to trace the report back to the original source.

We now add a `sourceKey` attribute to `Node` and report on it, to show
user's the key value as it was originally present in the source.
Remove both the `SnakeCaseParamMapper` and `KebabCaseParamMapper`
by default, add the `PathNormalizer` by default.

Add removal of `_` to path normalizer.

Fix some issues with the `HikariDataSourceDecoder` when enabling
path normalization by default -- that decoder requires the
original key case as its props are case-sensitive. Create an
abstract `UnnormalizedKeysDecoder` which has the ability to restore
the case of keys via the `sourceKey`.

Fix breaking explicit sealed types with normalization because the
discriminator field defaults to `_type` which normalizes to `type`.
Disable normalization if the field name matches the discriminator field
name, and the node is a `MapNode`.

Fix reporting for strict mode to use the `sourceKey` value, so that
reporting matches the source value, not the normalized value.

Update Preprocessor implementations to correctly copy the source key
when new Map and Array nodes are created.
@rocketraman
Copy link
Contributor Author

@sksamuel Just a ping on this PR, and the follow-up PRs that are in draft waiting for this one. Is there any plan to review and/or merge?

@sksamuel sksamuel merged commit a269ae5 into sksamuel:master Jul 15, 2024
1 check passed
@sksamuel
Copy link
Owner

I've been slow :)
Sorry

@sschuberth
Copy link
Contributor

Just wondering, does this new node transformer also help to implement #358?

@rocketraman
Copy link
Contributor Author

Just wondering, does this new node transformer also help to implement #358?

My first thought is no, but #414 would probably help a bit.

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.

3 participants