package co.smartreceipts.android.aws.cognito;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.hadisatrio.optional.Optional;
import co.smartreceipts.android.identity.IdentityManager;
import co.smartreceipts.android.identity.apis.me.Cognito;
import co.smartreceipts.android.identity.apis.me.User;
import co.smartreceipts.android.utils.log.Logger;
import io.reactivex.Single;
class CognitoIdentityProvider {
private final IdentityManager identityManager;
private final LocalCognitoTokenStore localCognitoTokenStore;
public CognitoIdentityProvider(@NonNull IdentityManager identityManager, @NonNull Context context) {
this(identityManager, new LocalCognitoTokenStore(context));
}
@VisibleForTesting
CognitoIdentityProvider(@NonNull IdentityManager identityManager, @NonNull LocalCognitoTokenStore localCognitoTokenStore) {
this.identityManager = Preconditions.checkNotNull(identityManager);
this.localCognitoTokenStore = Preconditions.checkNotNull(localCognitoTokenStore);
}
@NonNull
public Single<Optional<Cognito>> prefetchCognitoTokenIfNeeded() {
return Single.fromCallable(this::getCachedCognitoToken)
.flatMap(cognitoOptional -> {
if (!cognitoOptional.isPresent()
|| cognitoOptional.get().getCognitoToken() == null
|| cognitoOptional.get().getIdentityId() == null) {
Logger.debug(CognitoIdentityProvider.this, "Existing cognito token is invalid. Pre-fetching...");
return refreshCognitoToken();
} else {
Logger.debug(CognitoIdentityProvider.this, "Existing cognito token is valid");
return Single.just(cognitoOptional);
}
});
}
@NonNull
public Single<Optional<Cognito>> refreshCognitoToken() {
return this.identityManager.getMe()
.doOnSubscribe(disposable -> {
Logger.debug(CognitoIdentityProvider.this, "Clearing the cached cognito token to refresh");
localCognitoTokenStore.persist(null);
})
.<Optional<Cognito>>map(meResponse -> {
if (meResponse != null && meResponse.getUser() != null) {
Logger.debug(CognitoIdentityProvider.this, "Retrieve a valid token response");
final User user = meResponse.getUser();
return Optional.of(new Cognito(user.getCognitoToken(), user.getIdentityId(), user.getCognitoTokenExpiresAt()));
} else {
Logger.warn(CognitoIdentityProvider.this, "Failed to fetch a valid token");
return Optional.absent();
}
})
.doOnNext(optionalCognito -> {
if (optionalCognito.isPresent()) {
localCognitoTokenStore.persist(optionalCognito.get());
}else {
localCognitoTokenStore.persist(null);
}
})
.singleOrError();
}
@Nullable
public Cognito synchronouslyRefreshCognitoToken() {
try {
return refreshCognitoToken()
.onErrorReturn(throwable -> Optional.absent())
.blockingGet().get();
} catch (Exception e) {
return null;
}
}
@NonNull
public Optional<Cognito> getCachedCognitoToken() {
Cognito cognitoToken = localCognitoTokenStore.getCognitoToken();
return cognitoToken != null ? Optional.of(cognitoToken) : Optional.absent();
}
}