commercetools / commercetools-sdk-java-v2

The e-commerce SDK from commercetools for Java.
https://commercetools.github.io/commercetools-sdk-java-v2/javadoc/index.html
Apache License 2.0
34 stars 15 forks source link

Equivalent of SphereRequestDecorator? #283

Closed pintomau closed 2 years ago

pintomau commented 2 years ago

Sometimes, it may be useful to decorate specific requests, instead of the more generic middleware strategy.

One could create adapter classes that trigger the add and with methods, but we lose lazy evaluation (ie, trigger only when needed, on execution).

Is there already some straightforward way to achieve this that I'm missing?

The issue:

ApiMethod depends on ApiHttpClient that is not easily accessible from the ProjectApiRoot or other ApiMethods we intend to decorate.

Some suggestions to achieve this:

  1. Make ApiHttpClient accessible on ApiMethod or ProjectApiRoot.
  2. Add a facility to append such decorators/visitors to the ApiMethod (ie, something like addDecorator(UnaryOperator<T>), although T extends ApiMethod may be too broad as it also contains execute* methods).
jenschude commented 2 years ago

We would need to know what exactly you would like to extend.

The client instance has not been exposed in the ApiRoots to avoid accidental closing of the client which is AutoClosable.

Besides there are btw different options what also could be done. For example the ApiMethod allows to add any headers (withHeader, addHeader) and query parameters (withQueryParameter, addQueryParameter)

And the ApiRootBuilder allows to just build an ApiClient instance which itself could be wrapped inside another ApiClient.

ApiHttpClient client = ApiRootBuilder.of().buildClient();
ProjectApiRoot apiRoot = ProjectApiRoot.fromClient(client);

ApiHttpClient wrappedClient = ApiRootBuilder.of(client).buildClient();
ProjectApiRoot wrappedApiRoot = ProjectApiRoot.fromClient(wrappedClient);
pintomau commented 2 years ago

This is mainly a nitpick and perfectly "work-aroundable", feel free to close. The goal is to be able to create more portable and easier to reuse decorators across multiple projects. I think lazy evaluation of the decoration step is also a benefit we've lost.

Maybe, the better solution would be pulling up the methods only ApiMethod has to interfaces? For example, all execute and send* methods to (Client)RequestCommand? There are other methods within ApiMethod without interface, but not as necessary.

Regarding what we want to extend, it's what's already available in the api, all kinds of params, including sorting, paging, querying...

This would allow us to achieve something like:

var request = new MyDecorator3(new MyDecorator2(new MyDecorator(commercetoolsClient.productProjections().get(), args1...), args2...), args3...)...

// or, of course a composite decorator

return request.execute(...) // actual decoration takes place somewhere during the execution
  .thenApply(...);

// instead of 

// initialize decorators

ByProjectKeyProductProjectionsGet request = commercetoolsClient.productProjections().get();

// actual decoration takes place here
request = decorator3.decorate(decorator2.decorate(decorator1.decorate(request)));

return request.execute...

ProjectApiRoot wrappedApiRoot = ProjectApiRoot.fromClient(wrappedClient); does kind of solve it, but comes with a lot of baggage and is missing lazy evaluation.

Thanks.

jenschude commented 2 years ago

I added with #284 the option to apply a function or even multiple ones to the ApiHttpRequest after it has been build and before it gets executed.

Please see https://github.com/commercetools/commercetools-sdk-java-v2/blob/282a7af5c8eeec5e695e2fcb2d3ced8fa058670e/rmf/rmf-java-base/src/test/java/io/vrap/rmf/base/client/ApiMethodTest.java#L229-L247

jenschude commented 2 years ago

And I also added a method to apply a decorator function to the ApiMethod itself. See #285

jenschude commented 2 years ago

Version 8.3.0 has been released. The ApiMethod class now has methods which allow their decoration. Either by applying a lambda function to the ApiMethod instance or by apply a function at the ApiHttpRequest before being executed

Example usage can be seen here https://github.com/commercetools/commercetools-sdk-java-v2/blob/ac9ae00e51dd1889fc72c0bdfd26c694651ad995/rmf/rmf-java-base/src/test/java/io/vrap/rmf/base/client/ApiMethodTest.java#L232-L279