Skip to content

Commit

Permalink
Improve mappings documentation (#1212)
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Aug 28, 2024
1 parent fffd0da commit 9cc8776
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 11 deletions.
96 changes: 85 additions & 11 deletions documentation/src/main/docs/config/mappings.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ share the same prefix (or namespace). It supports the following set of features:
- Configuration Properties Naming Strategies
- Integration with Bean Validation

## Mapping Rules

A complex object type uses the following rules to map configuration values to their member values:

- A configuration path is built by taking the object type prefix (or namespace) and the mapping member name
- The member name is converted to its kebab-case format
- If the member name is represented as a getter, the member name is taken from its property name equivalent, and then
converted to its kebab-case format.
- The configuration value is automatically converted to the member type
- The configuration path is required to exist with a valid configuration value or the mapping will fail.

!!! info

Kebab-case - the method name is derived by replacing case changes with a dash to map the configuration property.

A Config Mapping requires an interface with minimal metadata configuration annotated with
`io.smallrye.config.ConfigMapping`:

Expand All @@ -26,8 +41,8 @@ method name with `.` (dot) as the separator.

!!! warning

If a mapping fails to match a configuration property a `NoSuchElementException` is thrown, unless the mapped
element is an `Optional`.
If a mapping fails to match a configuration property the config system throws a `NoSuchElementException`, unless
the mapped element is an `Optional`.

## Registration

Expand Down Expand Up @@ -68,12 +83,13 @@ Server server = config.getConfigMapping(Server.class);

!!! info

Config Mapping instances are cached. They are populated when the `Config` instance is initialized and their values
are not updated on Config Source changes.
Config Mapping instances are cached. They are populated when the `SmallRyeConfig` instance is initialized and
their values are not updated on `ConfigSource` changes.

For a Config Mapping to be valid, it needs to match every configuration property name contained in the `Config` under
the specified prefix set in `@ConfigMapping`. This prevents unknown configuration properties in the `Config`. This
behaviour can be disabled with the configuration `smallrye.config.mapping.validate-unknown=false`.
behaviour can be disabled with the configuration `smallrye.config.mapping.validate-unknown=false`, or by ignoring
specified paths with `io.smallrye.config.SmallRyeConfigBuilder.withMappingIgnore`.

## Defaults

Expand All @@ -97,6 +113,10 @@ return the value `bar`.

A nested mapping provides a way to map sub-groups of configuration properties.

- A nested type contributes with its name (converted to its kebab-case format)
- The configuration path is built by taking the root object type prefix (or namespace), the nested type name and the
member name of the nested type

```java
@ConfigMapping(prefix = "server")
public interface Server {
Expand Down Expand Up @@ -127,6 +147,35 @@ server.log.rotate=false
The method name of a mapping group acts as a sub-prefix in the property name. In this case the matching property to
`Server.Log#enabled` is `server.log.enabled`.

## Hierarchy

A config mapping can extend another mapping and inherit all its super members:

```java
public interface Parent {
String name();
}

@ConfigMapping(prefix = "child")
public interface Child extends Parent {

}
```

And override members:

```java
public interface Parent {
String name();
}

@ConfigMapping(prefix = "child")
public interface Child extends Parent {
@WithName("child-name")
String name();
}
```

## Overriding property names

### `@WithName`
Expand Down Expand Up @@ -328,7 +377,15 @@ A call to `Converters.foo()` results in the value `bar`.

## Collections

A config mapping is also able to map the collections types `List` and `Set`:
A config mapping is also able to map the collections types `List` and `Set`.

- A member with a `Collection` type requires the configuration name to be in its indexed format
- Each configuration name, plus its index maps the configuration value to the corresponding `Collection` element in the
object type
- The index must be part of the configuration path, by appending the index between square brackets to the`Collection`
member
- The index specified in the configuration name is used to order the element in the `Collection`
- Missing elements or gaps are removed

```java
@ConfigMapping(prefix = "server")
Expand Down Expand Up @@ -363,9 +420,18 @@ server.environments[0].apps[1].services=stock,warehouse
The `List` and `Set` mappings can use [Indexed Properties](indexed-properties.md) to map configuration values in
mapping groups.

!!! info

A `List` mapping is backed by an `ArrayList`, and a `Set` mapping is backed by a `HashSet`.

## Maps

A config mapping is also able to map a `Map`:
A config mapping is also able to map a `Map`.

- A member with a `Map` type requires an additional configuration name added to the configuration path of the `Map`
member to act as a map key
- The additional configuration name maps a Map entry with the configuration name as the `Map` entry key and
the configuration value as the Map entry value

```java
@ConfigMapping(prefix = "server")
Expand Down Expand Up @@ -399,6 +465,10 @@ The configuration property name needs to specify an additional segment to act as
the `Server#form` `Map` and the segments `index`, `login.page` and `error.page` represent the `Map`
keys.

!!! info

A `Map` mapping is backed by an `HashMap`.

For collection types, the key requires the indexed format. The configuration name `server.aliases.localhost[0].name`
maps to the `Map<String, List<Alias>> aliases()` member, where `localhost` is the `Map` key, `[0]` is the index of the
`List<Alias>` collection where the `Alias` element will be stored, containing the name `prod`.
Expand Down Expand Up @@ -474,10 +544,15 @@ Map<String, Alias> any = server.aliases.get("any");
Map<String, Alias> any = server.aliases.get("prod");
```

## ToString
## Optionals

- A mapping can wrap any complex type with an `Optional`
- `Optional` mappings do not require the configuration path and value to be present

If the config mapping contains a `toString` method declaration, the config mapping instance will include a proper
implementation of the `toString` method.
## toString, equals, hashcode

If the config mapping contains method declarations for `toString`, `equals` or `hashcode`, the config mapping instance
will include a proper implementation of these methods.

!!! caution

Expand Down Expand Up @@ -505,4 +580,3 @@ values do not follow the contraints defined in `Server`.
!!! info

For validation to work, the `smallrye-config-validator` dependency is required in the classpath.

Original file line number Diff line number Diff line change
Expand Up @@ -541,11 +541,36 @@ public SmallRyeConfigBuilder withMapping(Class<?> klass, String prefix) {
return this;
}

/**
* Ignores a specified path segment when performing a Config Mapping.
* <p>
* By default, a Config Mapping must match every configuration path available in the Config system. However, such
* conditions may not always be possible, and in that case, the specified path is ignored.
* <p>
* Examples of paths and ignores:
* <ul>
* <li><code>foo.bar</code> - ignores the configuration name <code>foo.bar</code></li>
* <li><code>foo.*</code> - ignores the single configurations names under <code>foo</code></li>
* <li><code>foo.**</code> - ignores all the configurations names under <code>foo</code></li>
* </ul>
*
* @param path the configuration path to ignore
* @return this {@link SmallRyeConfigBuilder}
* @see #withValidateUnknown(boolean)
*/
public SmallRyeConfigBuilder withMappingIgnore(String path) {
mappingsBuilder.ignoredPath(path);
return this;
}

/**
* Enable or disable the Config Mapping requirement to match every configuration path available in the Config
* system. By default, the validation is <b>enabled</b>.
*
* @param validateUnknown a boolean <code>true</code> to enable the validation, or <code>false</code> to disable it.
* @return this {@link SmallRyeConfigBuilder}
* @see #withValidateUnknown(boolean)
*/
public SmallRyeConfigBuilder withValidateUnknown(boolean validateUnknown) {
withDefaultValue(SmallRyeConfig.SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN, Boolean.toString(validateUnknown));
return this;
Expand Down

0 comments on commit 9cc8776

Please sign in to comment.