Skip to content

Commit

Permalink
docs: decision record about configuration injection (#4611)
Browse files Browse the repository at this point in the history
  • Loading branch information
paullatzelsperger authored Nov 11, 2024
1 parent 39c68a8 commit c6bb542
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Configuration injection

## Decision

In addition to service injection, the EDC project will support _configuration injection_ ("CI") in future releases.

## Rationale

In an effort to improve the ease-of-use and to lower the barrier of entry for developers we will provide a feature to
configure extensions with an annotation mechanism. Resolving configuration can result in convoluted and difficult to
read code.

## Approach

### Requirements

- default values: it should be possible to provide a default value for configuration fields
- optionality: configuration fields that are not required should be `null` in case there is no config value for them
- type flexibility: at least `String`, `Integer`, `Long`, `Double` and `Boolean` must be supported
- annotation-based: the configuration injection is triggered by annotations

Configuration values are resolved during runtime startup, more specifically during the dependency injection phase.
Technically they are another type of `InjectionPoint`.

### Resolving and injecting plain configuration values

Extension classes can declare fields of type `String`, `Integer`, `Long`, `Double` or `Boolean` and annotate them with
the `@Setting` annotation:

```java
public class SomeExtension implements ServiceExtension {

@Setting(key = "edc.iam.publickey.alias")
private String publicKeyAlias;
}
```

This would check if a config value `edc.iam.publickey.alias` is present on the `Config` object, and if so, assign its
value to the field. An injection error would be raised if no config value is found for `edc.iam.publickey.alias`. This
can be avoided by declaring a default value:

```java
public class SomeExtension implements ServiceExtension {

@Setting(key = "edc.iam.publickey.alias", defaultValue = "foobar")
private String publicKeyAlias;

@Setting(key = "edc.some.timeout", defaultValue = "60")
private Integer someTimeout; // default value gets converted to int

@Setting(key = "edc.some.fraction", defaultValue = "barbaz")
private Double someFraction; // runtime exception if the default value is used: "barbaz" cannot be converted to double
}
```

Note that default values are supplied as Strings, and an attempt is made to convert them to the appropriate type. An
injection error is raised raised if the value cannot be converted to the desired type.

Alternatively, config values can be marked as optional:

```java
public class SomeExtension implements ServiceExtension {

@Setting(key = "edc.iam.publickey.alias", required = false)
private String publicKeyAlias;
}
```

If no config value is found for `edc.iam.publickey.alias`, then the field is `null`.

Note that if a `defaultValue` is supplied, the `required` attribute becomes meaningless.

### Resolving and injecting configuration objects

In addition to supplying simple config values it should be possible to have the injection mechanism construct a _config
object_:

```java
public class SomeExtension implements ServiceExtension {

@Configuration
private KeyConfig keyConfig;
}

@Settings
public record KeyConfig(@Setting(key = "edc.iam.publickey.alias") String alias,
@Setting(key = "edc.iam.key.algorithm") String algorithm) {
}

// alternatively:
@Settings
public class KeyConfig {

@Setting(key = "edc.iam.publickey.alias")
private String alias;

@Setting(key = "edc.iam.key.algorithm")
private String algorithm;

// MUST have a public default constructor!
}
```

These are Java POJOs that are annotated with the `@Settings` annotation and contain the actual config values. The same
principle applies as before, but the `@Setting`-annotated fields are compounded in a class or a `record`. However,
some limitations apply:

- the field must be annotated with `@Configuration` and the class must be annotated with `@Settings`
- they can only be declared inside an extension class
- config record classes can only contain constructors where every parameter is annotated with `@Setting`
- `@Configuration`-annotated fields are optional if and only if **all** `@Setting`-annotated fields within them have the
`required = false` attribute. This optionality is _implicit_ and cannot be configured.
- `@Configuration`-annotated fields **cannot** have default values because those would have to be compile-time constant.
However, all config values _within_ can have default values.
- all `@Setting`-annotated fields of a `@Configuration` object must be optional or resolvable, either from config or via
a default value, otherwise the CI mechanism fails with an error
- if config objects are normal classes, they **must** have a public default constructor. All other constructors are
ignored by the CI mechanism
- `@Setting`-annotated fields must not be `final`
- nested config objects are not supported

### Changes to the `autodoc` processor

We already have a `@Setting` annotation in the `runtime-metamodel` component, which we should re-use for this. A new
attribute named `key` is added to the `@Setting` annotation. If present, it triggers the config injection mechanism.

Currently, the description is the default `value()` attribute of the `@Setting` annotation. This should eventually be
changed so that the `key` becomes the default value, and `description` is another named attribute.

### Error reporting

By piggy-backing on the dependency injection mechanism, configuration injection errors are automatically reported in the
same way as service injection errors.
Reporting dependency errors will be improved in future development iterations.
1 change: 1 addition & 0 deletions docs/developer/decision-records/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@
- [2024-10-06 Typed Policy Scopes through Contexts](./2024-10-05-typed-policy-context)
- [2024-10-10 DAPS module deprecation](./2024-10-10-daps-deprecation)
- [2024-10-24 bidirectional-data-transfers](./2024-10-24-bidirectional-data-transfers)
- [2024-11-06 configuration-injection](./2024-11-06-configuration-injection)

0 comments on commit c6bb542

Please sign in to comment.