I want to create a construct that would work with pageable feign
api calls and dry them from the first page of declared size available to the last one.
To take in account:
This is what I did:
method that is a base for draining a particular api call:
public <T> List<BaseFeignResult<T>> drainFeignPageableCall(
PagedCall<T> feignCall
) {
BaseFeignResult<T> firstPage = feignCall.call(0, 10);
List<BaseFeignResult<T>> baseFeignResults = drainFeignPageableCall(feignCall, firstPage, Lists.newArrayList(firstPage), 1);
return baseFeignResults;
}
It's overload and continuation:
<T> List<BaseFeignResult<T>> drainFeignPageableCall(
PagedCall<T> feignCall,
BaseFeignResult<T> dataPage,
List<BaseFeignResult<T>> acc,
int page
) {
if (dataPage.resp.getBody().getData().size() % 10 > 0)
return acc;
BaseFeignResult<T> res = feignCall.call(page, 10);
acc.add(res);
return drainFeignPageableCall(feignCall, res, acc, ++page);
}
And the definitions:
public static class SingleParamPageableCall<T> implements PagedCall<T> {
SingleParamPagingApi<T> fun;
String param;
public SingleParamPageableCall(SingleParamPagingApi<T> fun, String param) {
this.fun = fun;
this.param = param;
}
@Override
public BaseFeignResult<T> call(int p, int s) {
BaseFeignResult.BaseFeignResultBuilder<T> builder = BaseFeignResult.builder();
try {
builder.resp(fun.callFeignApi(param, p, s));
} catch (RuntimeException e) {
builder.excp(e);
}
return builder.build();
}
}
public interface PagedCall<T> {
BaseFeignResult<T> call(int p, int s);
}
@Builder
public static class BaseFeignResult<T> {
private final ResponseEntity<IVDPagedResponseOf<T>> resp;
private final RuntimeException excp;
}
public interface SingleParamPagingApi<T> {
ResponseEntity<IVDPagedResponseOf<T>> callFeignApi(String arg, int page, int size) throws RuntimeException;
}
This can be arbitraliry called as:
drainFeignPageableCall(new BaseService.SingleParamPageableCall<GetOrderInfoDto>(ordersFeignClient::getOrdersBySampleIds, "34596"));
and works as expected.
So as you can see, if I want to keep some sort of abstraction above various drain-able per api calls, I need to introduce definitions like SingleParamPagingApi
and class implementation of SingleParamPageableCall<T>
. so with every other api to be treated this way, I would need to redefine those.
My question here is: how to do this in purely descripive way, or how to reimplement this as a functional programming? to be clear: I would like to have code impl. in which I would describe how to map parameters to the method call (that can and will vary) and return a common data structure with the data being of generic type.
Basically I am looking for the most descriptive way of re-implementing this in Java without defining heavy objects like SingleParamPagingApi<T>
, but describing how to mount params called with to API params itself rather.
Thank you!
This simplest way would be to replace your SingleParamPagingApi
interface with one that has a method that just takes the page no and size as parameters (PagingApi
). And replace SingleParamPageableCall
with a class that just takes a PagingApi
argument. Then you can create the variants of PagingApi
for 1 parameter, 2 parameters etc by immediately binding the method to the argument 0, argument 1 etc, thereby creating a PagingApi
instance (the of
methods).
public interface PagingApi1<T, A0> {
ResponseEntity<IVDPagedResponseOf<T>> callFeignApi(A0 arg0, int page, int size) throws RuntimeException;
}
public interface PagingApi2<T, A0, A1> {
ResponseEntity<IVDPagedResponseOf<T>> callFeignApi(A0 arg0, A1 arg1, int page, int size) throws RuntimeException;
}
public interface PagingApi<T> {
static <T, A0> PagingApi<T> of(PagingApi1<T, A0> api, A0 arg0) {
return (p, s) -> api.callFeignApi(arg0, p, s);
}
static <T, A0, A1> PagingApi<T> of(PagingApi2<T, A0, A1> api, A0 arg0, A1 arg1,) {
return (p, s) -> api.callFeignApi(arg0, arg1, p, s);
}
ResponseEntity<IVDPagedResponseOf<T>> callFeignApi(int page, int size) throws RuntimeException;
}
public static class PageableCall<T> implements PagedCall<T> {
PagingApi<T> fun;
public PageableCall(PagingApi<T> fun) {
this.fun = fun;
}
@Override
public BaseFeignResult<T> call(int p, int s) {
BaseFeignResult.BaseFeignResultBuilder<T> builder = BaseFeignResult.builder();
try {
builder.resp(fun.callFeignApi(p, s));
} catch (RuntimeException e) {
builder.excp(e);
}
return builder.build();
}
}
You would call it as follows:
drainFeignPageableCall(
new PageableCall<GetOrderInfoDto>(
PagingApi.of(ordersFeignClient::getOrdersBySampleIds, "34596")
)
);
As a further simplifcation, you could probably collapse PagingApi
and PagedCall
into a single interface.
I would also suggest replacing the recursive calls in drainFeignPageableCall
with a simple for loop. You might think recursion is more "functional" but it's needlessly complex and inefficient here.