Quarkus & Microprofile : Is there a better way to use a property from application.properties into @ClientHeaderParam?
Asked Answered
P

2

6

I'm trying to build a simple app that calls an API with quarkus-rest-client. I have to inject an API Key as a header which is the same for all resources of the API. So I would like to put the value of this API Key (that depends on the environment dev/qa/prod) in the application.properties file located in src/main/resources.

I tried different ways to achieve this:

  • Use directly com.acme.Configuration.getKey into @ClientHeaderParam value property
  • Create a StoresClientHeadersFactory class which implements ClientHeadersFactory interface to inject the configuration

Finally, I found the way described below to make it work.

My question is: Is there a better way to do it?

Here's my code:

  • StoreService.java which is my client to reach the API
@Path("/stores")
@RegisterRestClient
@ClientHeaderParam(name = "ApiKey", value = "{com.acme.Configuration.getStoresApiKey}")
public interface StoresService {

    @GET
    @Produces("application/json")
    Stores getStores();

}
  • Configuration.java
@ApplicationScoped
public class Configuration {

    @ConfigProperty(name = "apiKey.stores")
    private String storesApiKey;

    public String getKey() {
        return storesApiKey;
    }

    public static String getStoresApiKey() {
        return CDI.current().select(Configuration.class).get().getKey();
    }

}
  • StoresController.java which is the REST controller
@Path("/stores")
public class StoresController {

    @Inject
    @RestClient
    StoresService storesService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Stores getStores() {
        return storesService.getStores();
    }

}
Pergola answered 25/10, 2019 at 12:34 Comment(4)
Can you provide some more detail on why using StoresClientHeadersFactory didn't work? Agree it's not pretty, but at least it works. We could look to modify the specification to support string values, config values, or methods in the annotation, if that would help?Lionfish
I agree it works! I asked to know if there is a better solution that I didn't find. thanks for your reply Regarding StoresClientHeadersFactory: @Inject Configuration applicationConfigurationdoesn't work. The object is null when I use it into StoresClientHeadersFactory.update(inboundHeaders, outboudHeaders) methodPergola
Ok, I suspect that's because the class isn't a CDI bean, so injection doesn't work. Can you raise an issue: github.com/quarkusio/quarkus/issuesLionfish
github.com/quarkusio/quarkus/issues/5030Pergola
H
9

Late to the party, but putting this here for my own reference. There seems to be a difference in using @ClientHeaderParam and @HeaderParam, so I investigated a little further: According to the Microprofile docs, you can put a compute method for the value in curly braces. This method can extract the property value.

See link for more examples.

EDIT: What I came up with resembles the original, but uses a default method on the interface, so you can at least discard the Configuration class. Also, using the org.eclipse.microprofile.config.Config and ConfigProvider classes to get the config value:

@RegisterRestClient
@ClientHeaderParam(name = "Authorization", value = "{getAuthorizationHeader}")
public interface StoresService {

    default String getAuthorizationHeader(){
        final Config config = ConfigProvider.getConfig();
        return config.getValue("apiKey.stores", String.class);
    }

    @GET
    @Produces("application/json")
    Stores getStores();

Holocene answered 19/5, 2020 at 11:53 Comment(0)
C
4

I will get rid of the Configuration class and use an @HeaderParam to pass your configuration property from your rest endpoint to your rest client. The annotation will then send this property as an HTTP header to the remote service.

Somthing like this should works:

@Path("/stores")
@RegisterRestClient
public interface StoresService {

    @GET
    @Produces("application/json")
    Stores getStores(@HeaderParam("ApiKey") storesApiKey);

}

@Path("/stores")
public class StoresController {
    @ConfigProperty(name = "apiKey.stores")
    private String storesApiKey;

    @Inject
    @RestClient
    StoresService storesService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Stores getStores() {
        return storesService.getStores(storesApiKey);
    }

}
Citizenship answered 30/10, 2019 at 11:6 Comment(1)
This would work, but the other solution is cleaner.Bagel

© 2022 - 2024 — McMap. All rights reserved.