Micronaut @ConfigurationProperties not loading in a List of pojos correctly

59 Views Asked by At

I have the following property in my application.yml file:

payment-routing-config:
  drivers:
    - id: 'ANETC'
      weight: 80
      backups: ['EPAYC', 'NMIC', 'ANETD']
    - id: 'EPAYC'
      weight: 80
      backups: ['ANETC', 'NMIC', 'ANETD']
    - id: 'NMI'
      weight: 80
      backups: ['EPAYC', 'ANETC', 'ANETD']

And the following Java:

@ConfigurationProperties("paymentRoutingConfig")
public record PaymentRoutingConfig(
    List<PaymentDriver> drivers
) { }

record PaymentDriver(
    String id,
    int weight,
    List<String> backups
) { }

When I use @Inject to load in the config, the drivers property is an empty list. If I change the List drivers to List drivers I populates the following object:

PaymentRoutingConfig[drivers=[{id=ANETC, weight=80, backups=[EPAYC, NMIC, ANETD]}, {id=EPAYC, weight=80, backups=[ANETC, NMIC, ANETD]}, {id=NMI, weight=80, backups=[EPAYC, ANETC, ANETD]}]]

This is how i inject it into my controller:

@Inject
PaymentRoutingConfig paymentRoutingConfig;

Are complex objects unable to be used with @ConfigurationProperties?

I even tried simplifying List to a single object:

@ConfigurationProperties("paymentRoutingConfig")
public record PaymentRoutingConfig(
    PaymentDriver driver
) { }

record PaymentDriver(
    String id,
    int weight
) { }

yaml:

payment-routing-config:
  driver:
    id: 'test'
    weight: 10

And I get this error:

Message: Failed to inject value for parameter [driver] of class: com.***.paymentservice.infrastructure.PaymentRoutingConfig

Message: Error resolving property value [payment-routing-config.driver]. Property doesn't exist
Path Taken: new PaymentController() --> PaymentController.paymentRoutingConfig --> new PaymentRoutingConfig([PaymentDriver driver])
Path Taken: new PaymentController() --> PaymentController.paymentRoutingConfig --> new PaymentRoutingConfig([PaymentDriver driver])
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [driver] of class: com.***.paymentservice.infrastructure.PaymentRoutingConfig
2

There are 2 best solutions below

0
testing123 On BEST ANSWER

Using the following in my yaml file I was able to populate the config object correctly:

payment-routing-config:
  drivers: [
    {id: 'test', weight: 10, backups: ['EPAYC', 'ANETC', 'ANETD']},
    {id: 'test2', weight: 20, backups: ['ANETC', 'EPAYC', 'ANETD']}
  ]
0
Gebezs On

I think you need the @EachProperty with the list = true settings.

package com.example;

import static org.junit.jupiter.api.Assertions.assertEquals;

import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.EachProperty;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import java.util.List;
import lombok.Data;
import org.junit.jupiter.api.Test;

@MicronautTest(propertySources = "classpath:each-bean-list.yml")
public class EachBeanListTest {

  @Test
  void list(PaymentRoutingConfig config) {
    assertEquals(3, config.getDrivers().size());
    assertEquals(1, config.getDrivers().stream()
      .filter(paymentDriver -> paymentDriver.getId().equals("ANETC"))
      .count());
  }

  @Data
  @ConfigurationProperties("payment-routing-config")
  public static class PaymentRoutingConfig {
    private List<PaymentDriver> drivers;

    @Data
    @EachProperty(value = "drivers", list = true)
    public static class PaymentDriver {
      private String id;
      private int weight;
      private List<String> backups;
    }
  }
}

The each-bean-list.yml:

payment-routing-config:
  drivers:
    - id: 'ANETC'
      weight: 80
      backups: ['EPAYC', 'NMIC', 'ANETD']
    - id: 'EPAYC'
      weight: 80
      backups: ['ANETC', 'NMIC', 'ANETD']
    - id: 'NMI'
      weight: 80
      backups: ['EPAYC', 'ANETC', 'ANETD']

You can also use @EachProperty with map-like YAML, however the key will be converted to lower-case, which might be a show stopper.

package com.example;

import static org.junit.jupiter.api.Assertions.assertEquals;

import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.EachProperty;
import io.micronaut.context.annotation.Parameter;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import java.util.List;
import lombok.Data;
import org.junit.jupiter.api.Test;

@MicronautTest(propertySources = "classpath:each-bean-map.yml")
public class EachBeanMapTest {

  @Test
  void map(PaymentRoutingConfig config) {
    System.out.println(config);
    assertEquals(3, config.getDrivers().size());
    assertEquals(1, config.getDrivers().stream()
        .filter(paymentDriver -> paymentDriver.getId().equals("anetc"))
        .count());
  }

  @Data
  @ConfigurationProperties("payment-routing-config")
  public static class PaymentRoutingConfig {
    private List<PaymentDriver> drivers;

    @Data
    @EachProperty(value = "drivers")
    public static class PaymentDriver {
      public PaymentDriver(@Parameter String id) {
        this.id = id;
      }

      private String id;
      private int weight;
      private List<String> backups;
    }
  }
}

The each-bean-map.yml:

payment-routing-config:
  drivers:
    ANETC:
      weight: 80
      backups: ['EPAYC', 'NMIC', 'ANETD']
    EPAYC:
      weight: 80
      backups: ['ANETC', 'NMIC', 'ANETD']
    NMI:
      weight: 80
      backups: ['EPAYC', 'ANETC', 'ANETD']

You can find more information in the respective part of the Micronaut Guide.