0

finally after extensive stack-overflowing ;-) and debugging I made it work: My Feign-client can make requests on Spring-Data-Rest's API and I get a Resource<Something> with filled links back.

My code so far...

The FeignClient:

@FeignClient(name = "serviceclient-hateoas",
    url = "${service.url}",
    decode404 = true,
    path = "${service.basepath:/api/v1}",
    configuration = MyFeignHateoasClientConfig.class)
public interface MyFeignHateoasClient {

    @RequestMapping(method = RequestMethod.GET, path = "/bookings/search/findByBookingUuid?bookingUuid={uuid}")
    Resource<Booking> getBookingByUuid(@PathVariable("uuid") String uuid);

}

The client-config:

@Configuration
public class MyFeignHateoasClientConfig{

    @Value("${service.user.name:bla}")
    private String serviceUser;

    @Value("${service.user.password:blub}")
    private String servicePassword;

    @Bean
    public BasicAuthRequestInterceptor basicAuth() {
        return new BasicAuthRequestInterceptor(serviceUser, servicePassword);
    }

    @Bean
    public Decoder decoder() {
        return new JacksonDecoder(getObjectMapper());
    }

    @Bean
    public Encoder encoder() {
        return new JacksonEncoder(getObjectMapper());
    }

    public ObjectMapper getObjectMapper() {
        return new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModule(new Jackson2HalModule());
    }

    @Bean
    public Logger logger() {
        return new Slf4jLogger(MyFeignHateoasClient.class);
    }

    @Bean
    public Logger.Level logLevel() {
        return Logger.Level.FULL;
    }
}

And in the application using the client via an jar-dependency:

@SpringBootApplication
@EnableAutoConfiguration
@EnableFeignClients(basePackageClasses=MyFeignHateoasClient.class)
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
@ComponentScan(excludeFilters = @Filter(type = ... ), basePackageClasses= {....class}, basePackages="...")
public class Application {
...

Now this is working:

@Autowired
private MyFeignHateoasClient serviceClient;
...
void test() {
    Resource<Booking> booking = serviceClient.getBookingByUuid(id);
    Link link = booking.getLink("relation-name");
}

Now my question:

How do I go on from here, i.e. navigate to the resource in the Link?

The Link is containing an URL on the resource I want to request.

  • Do I really have to parse the ID out of the URL and add a method to the FeignClient like getRelationById(id)
  • Is there at least a way to pass the complete resource-url to a method of a FeignClient?

I have found no examples which demonstrate how to proceed from here (despite the POST/modify). Any hints appreciated!

Thx

Piero-V
  • 1
  • 2

1 Answers1

0

My current solution:

I added an additional request in the Feign client, taking the whole resource path:

...
public interface MyFeignHateoasClient {
...
    @RequestMapping(method = RequestMethod.GET, path = "{resource}")
    Resource<MyLinkedEntity> getMyEntityByResource(@PathVariable("resource") String resource);
}

Then I implemented some kind of "HAL-Tool":

...                                                                                                                                                                              
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import org.springframework.hateoas.Link;

import feign.Target;
import lombok.SneakyThrows;

public class HalTool {

    private Object feignClient;

    public static HalTool forClient( Object feignClient ) {
        return new HalTool(feignClient);
    }

    private HalTool( Object feignClient ) {
        this.feignClient = feignClient;
    }

    @SneakyThrows
    private String getUrl() {
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(feignClient);
        Field target = invocationHandler.getClass().getDeclaredField("target");
        target.setAccessible(true);
        Target<?> value = (Target<?>) target.get(invocationHandler);
        return value.url();
    }

    public String toPath( Link link ) {
        String href = link.getHref();
        String url = getUrl();
        int idx = href.indexOf(url);
        if (idx >= 0 ) {
            idx += url.length();
        }
        return href.substring(idx);
    }
}                                                                                                                                                                                         

And then I could do request a linked resource like this:

Link link = booking.getLink("relation-name");
Resource<MyLinkedEntity> entity = serviceClient.getMyEntityByResource(
    HalTool.forClient(serviceClient).toPath(link));
Piero-V
  • 1
  • 2
  • In the meantime I removed the reflection stuff and build a small wrapper around the feign client where I inject the `service.url` and `path`. This has advantages when it comes to testing. – Piero-V Jan 25 '18 at 08:28