vert-x3 / vertx-service-proxy

EventBus Proxy generation
Apache License 2.0
67 stars 58 forks source link

Generated Proxies need to allow setting headers #139

Open pmlopes opened 1 year ago

pmlopes commented 1 year ago

The current proxy generator is very simple and does not take into consideration the headers in the event bus message. In a scenario where a user would want to send a Authentication token with the message to attest the id of the caller, this is currently not supported and requires the user to manually perform the marshalling, e.g.:

service = new MicroServiceVertxEBProxy(vertx, MicroService.ADDRESS);

vertx.eventBus()
              .<String>request(MicroService.ADDRESS, new JsonObject(), new DeliveryOptions()
                .addHeader("action", "execute")
                .addHeader("auth-token", ctx.user().get("id_token")));

This example is simple as the service method execute takes no arguments. This approach avoids the current generator that is unable to set headers and would like like:

service = new MicroServiceVertxEBProxy(vertx, MicroService.ADDRESS);

service.execute()

In order to address this we should have a better generated proxy, for example it should be usable as:

service = new MicroServiceVertxEBProxy(vertx, MicroService.ADDRESS);

service
  .execute()
  .call(deliveryOptions -> { ... })

Here in the optional argument of call, one can capture the request, (say we're on vertx-web) and add the required headers, e.g.:

.call(deliveryOptions -> deliveryOptions.addHeader("auth-token", ctx.user().get("id_token")))

In order to achive this we would need some abstract proxy class:

public abstract AbstractVertxEBProxy {
  private Vertx _vertx;
  private String _address;
  private DeliveryOptions _options;
  private boolean closed;

  public MicroServiceVertxEBProxy(Vertx vertx, String address) {
    this(vertx, address, null);
  }

  public MicroServiceVertxEBProxy(Vertx vertx, String address, DeliveryOptions options) {
    this._vertx = vertx;
    this._address = address;
    this._options = options;
    try {
      this._vertx.eventBus().registerDefaultCodec(ServiceException.class, new ServiceExceptionMessageCodec());
    } catch (IllegalStateException ex) {
    }
  }

  public final class Caller<T> {

    private final String _action;
    private final JsonObject _json;
    private final io.vertx.core.Future<T> _future;

    private Caller(io.vertx.core.Future<T> future) {
      this._action = null;
      this._future = future;
      this._json = null;
    }

    private Caller(String _action, JsonObject _json) {
      this._action = _action;
      this._json = _json;
      this._future = null;
    }

    public Future<T> call() {
      return call(null);
    }

    public Future<T> call(Consumer<DeliveryOptions> postage) {
      if (_action == null) {
        return _future;
      }

      DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions();

      if (postage != null) {
        try {
          postage.accept(_deliveryOptions);
        } catch (RuntimeException e) {
          return Future.failedFuture(e);
        }
      }

      MultiMap headers = _deliveryOptions.getHeaders();
      if (headers == null) {
        _deliveryOptions.addHeader("action", _action);
      } else {
        _deliveryOptions.getHeaders().set("action", _action);
      }

      return _vertx.eventBus().<T>request(_address, _json, _deliveryOptions)
        .map(msg -> msg.body());
    }
  }
}

Then the generator would generate the following code:

// for each method in the @ProxyGen interface

  //@Override
  public Caller<String> method_name() { // <-- note Caller instead of `Future`
    if (closed) return new Caller<>(io.vertx.core.Future.failedFuture("Proxy is closed"));
    // encoding to JSON as usual...
    // ...
    return new Caller<>("execute", _json);
  }

With this approach we can now annotate the message with the headers we wish.

pmlopes commented 1 year ago

Extra helpers could be added. Like in the example above, we could have a helper just for passing the auth-token as:

service = new MicroServiceVertxEBProxy(vertx, MicroService.ADDRESS);

service
  .execute()
  .authorizeAndCall(ctx.user().get("id_token"))