All Downloads are FREE. Search and download functionalities are using the official Maven repository.

vertx.effect.core.OauthModule Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
package vertx.effect.core;

import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpClientOptions;
import jsonvalues.JsObj;
import vertx.effect.Val;
import vertx.effect.exp.All;
import vertx.effect.exp.Cons;
import vertx.effect.exp.IfElse;
import vertx.effect.httpclient.*;
import vertx.effect.λ;
import vertx.effect.λc;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

public abstract class OauthModule extends HttpClientModule {

    protected String accessToken;

    protected BiFunction> accessTokenReq;
    protected final λ readNewAccessTokenAfterRefresh;
    protected final int accessTokenReqAttempts;
    protected final String authorizationHeaderName;
    protected final Function authorizationHeaderValue;

    protected final Predicate refreshTokenPredicate;
    protected final Predicate retryAccessTokenReqPredicate;
    protected final Predicate retryReqPredicate;

    public OauthModule(final HttpClientOptions options,
                       final String address,
                       final String authorizationHeaderName,
                       final Function authorizationHeaderValue,
                       final λ readNewAccessTokenAfterRefresh,
                       final Predicate refreshTokenPredicate,
                       final Predicate retryAccessTokenReqPredicate,
                       final Predicate retryReqPredicate,
                       final int accessTokenReqAttempts,
                       final int reqAttempts
                      ) {

        super(options,
              address
             );

        this.readNewAccessTokenAfterRefresh = readNewAccessTokenAfterRefresh;
        this.authorizationHeaderName = authorizationHeaderName;
        this.authorizationHeaderValue = authorizationHeaderValue;
        this.refreshTokenPredicate = refreshTokenPredicate;
        this.retryAccessTokenReqPredicate = retryAccessTokenReqPredicate;
        this.retryReqPredicate = retryReqPredicate;
        this.accessTokenReqAttempts = accessTokenReqAttempts;
        this.getOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                              reqAttempts,
                              false
                             );

        this.postOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                               reqAttempts,
                               false
                              );

        this.putOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                              reqAttempts,
                              false
                             );

        this.deleteOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                                 reqAttempts,
                                 false
                                );

        this.patchOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                                reqAttempts,
                                false
                               );

        this.headOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                               reqAttempts,
                               false
                              );

        this.connectOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                                  reqAttempts,
                                  false
                                 );

        this.optionsOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                                  reqAttempts,
                                  false
                                 );

        this.traceOauth = oauth((context, reqParams) -> httpClient.apply(reqParams.createHttpReq()),
                                reqAttempts,
                                false
                               );
    }


    public final λc getOauth;
    public final λc postOauth;
    public final λc putOauth;
    public final λc deleteOauth;
    public final λc patchOauth;
    public final λc headOauth;
    public final λc connectOauth;
    public final λc optionsOauth;
    public final λc traceOauth;

    protected > λc resilientReq(final λc req,
                                                               final int reqAttempts
                                                              ) {

        return (context, reqParams) -> req.apply(context,
                                                 reqParams
                                                )
                                          .recoverWith(e ->
                                                               IfElse.predicate(All.of(retryReqPredicate.test(e),
                                                                                              reqAttempts > 0
                                                                                             )
                                                                                      )
                                                                       .consequence(this.resilientReq(req,
                                                                                                      reqAttempts - 1
                                                                                                     )
                                                                                        .apply(reqParams)
                                                                                   )
                                                                       .alternative(Cons.failure(e))
                                                      )
                                          .flatMap(resp ->
                                                           IfElse.predicate(Cons.success(refreshTokenPredicate.test(resp)))
                                                                   .consequence(this.oauth(req,
                                                                                           reqAttempts - 1,
                                                                                           true
                                                                                          )
                                                                                    .apply(reqParams)
                                                                               )
                                                                   .alternative(Cons.success(resp))


                                                  );


    }

    protected > λc oauth(final λc req,
                                                        final int reqAttempts,
                                                        final boolean refreshToken
                                                       ) {
        return (context, reqParams) ->
                //really important: Cons.of instead of Cons.success to capture the state of this.accessToken
                IfElse.predicate(Cons.of(()->Future.succeededFuture(refreshToken || accessToken == null)))
                        .consequence(
                                accessTokenReq.apply(context,
                                                     this
                                                    )
                                              .flatMap(readNewAccessTokenAfterRefresh)
                                              .retry(retryAccessTokenReqPredicate,
                                                     accessTokenReqAttempts
                                                    )
                                              .onSuccess(newToken -> this.accessToken = newToken)
                                    )
                        //really important: Cons.of instead of Cons.success to capture the state of this.accessToken
                        .alternative(Cons.of(()-> Future.succeededFuture(this.accessToken)))
                        .flatMap(token -> resilientReq(req,
                                                       reqAttempts
                                                      ).apply(reqParams.setHeader(authorizationHeaderName,
                                                                                  authorizationHeaderValue.apply(token)
                                                                                 )
                                                             )
                                );
    }



}