"use strict";
/**
 * @fileoverview App Auth Box API Session.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const assert_1 = __importDefault(require("assert"));
const bluebird_1 = require("bluebird");
const errors_1 = __importDefault(require("../util/errors"));
// ------------------------------------------------------------------------------
// Private
// ------------------------------------------------------------------------------
/**
 * Validate that an object is a valid TokenStore object
 *
 * @param {Object} obj the object to validate
 * @returns {boolean} returns true if the passed in object is a valid TokenStore object that
 * has all the expected properties. false otherwise.
 * @private
 */
function isObjectValidTokenStore(obj) {
    return Boolean(obj && obj.read && obj.write && obj.clear);
}
// ------------------------------------------------------------------------------
// Public
// ------------------------------------------------------------------------------
/**
 * App Auth Box API Session.
 *
 * The App Auth API Session holds an accessToken for an app user or enterprise,
 * which it returns to the client so that it may make calls on behalf of
 * these entities.
 *
 * These access tokens will be refreshed in the background if a request is made within the
 * "stale buffer" (defaults to 10 minutes before the token is set to expire).
 * If the token is also expired, all incoming requests will be held until a fresh token
 * is retrieved.
 *
 * @param {string} type The type of the entity to authenticate the app auth session as, "user" or "enterprise"
 * @param {string} id The Box ID of the entity to authenticate as
 * @param {Config} config The SDK configuration options
 * @param {TokenManager} tokenManager The TokenManager
 * @param {TokenStore} [tokenStore] The token store instance to use for caching token info
 * @constructor
 */
class AppAuthSession {
    constructor(type, id, config, tokenManager, tokenStore) {
        this._type = type;
        this._id = id;
        this._config = config;
        this._tokenManager = tokenManager;
        // If tokenStore was provided, set the persistent data & current store operations
        if (tokenStore) {
            (0, assert_1.default)(isObjectValidTokenStore(tokenStore), 'Token store provided is improperly formatted. Methods required: read(), write(), clear().');
            this._tokenStore = bluebird_1.Promise.promisifyAll(tokenStore);
        }
        // The TokenInfo object for this app auth session
        this._tokenInfo = null;
        // Indicates if tokens are currently being refreshed
        this._refreshPromise = null;
    }
    /**
     * Initiate a refresh of the app auth access tokens. New tokens should be passed
     * to the caller, and then cached for later use.
     *
     * @param {TokenRequestOptions} [options] - Sets optional behavior for the token grant
     * @returns {Promise<string>} Promise resolving to the access token
     * @private
     */
    _refreshAppAuthAccessToken(options) {
        // If tokens aren't already being refreshed, start the refresh
        if (!this._refreshPromise) {
            this._refreshPromise = this._tokenManager
                .getTokensJWTGrant(this._type, this._id, options)
                .then((tokenInfo) => {
                // Set new token info and propagate the new access token
                this._tokenInfo = tokenInfo;
                if (this._tokenStore) {
                    return this._tokenStore
                        .writeAsync(tokenInfo)
                        .then(() => tokenInfo.accessToken);
                }
                return tokenInfo.accessToken;
            })
                .finally(() => {
                // Refresh complete, clear promise
                this._refreshPromise = null;
            });
        }
        return this._refreshPromise;
    }
    /**
     * Produces a valid, app auth access token.
     * Performs a refresh before returning if the current token is expired. If the current
     * token is considered stale but still valid, return the current token but initiate a
     * new refresh in the background.
     *
     * @param {TokenRequestOptions} [options] - Sets optional behavior for the token grant
     * @returns {Promise<string>} Promise resolving to the access token
     */
    getAccessToken(options) {
        var expirationBuffer = this._config.expiredBufferMS;
        // If we're initializing the client and have a token store, try reading from it
        if (!this._tokenInfo && this._tokenStore) {
            return this._tokenStore.readAsync().then((tokenInfo) => {
                if (!this._tokenManager.isAccessTokenValid(tokenInfo, expirationBuffer)) {
                    // Token store contains expired tokens, refresh
                    return this._refreshAppAuthAccessToken(options);
                }
                this._tokenInfo = tokenInfo;
                return tokenInfo.accessToken;
            });
        }
        // If the current token is not fresh, get a new token. All incoming
        // requests will be held until a fresh token is retrieved.
        if (!this._tokenInfo ||
            !this._tokenManager.isAccessTokenValid(this._tokenInfo, expirationBuffer)) {
            return this._refreshAppAuthAccessToken(options);
        }
        // Your token is not currently stale! Return the current access token.
        return bluebird_1.Promise.resolve(this._tokenInfo.accessToken);
    }
    /**
     * Revokes the app auth token used by this session, and clears the saved tokenInfo.
     *
     * @param {TokenRequestOptions} [options]- Sets optional behavior for the token grant
     * @returns {Promise} Promise resolving if the revoke succeeds
     */
    revokeTokens(options) {
        // The current app auth token is revoked (but a new one will be created automatically as needed).
        var tokenInfo = this._tokenInfo || {}, accessToken = tokenInfo.accessToken;
        this._tokenInfo = null;
        return this._tokenManager.revokeTokens(accessToken, options);
    }
    /**
     * Exchange the client access token for one with lower scope
     * @param {string|string[]} scopes The scope(s) requested for the new token
     * @param {string} [resource] The absolute URL of an API resource to scope the new token to
     * @param {Object} [options] - Optional parameters
     * @param {TokenRequestOptions} [options.tokenRequestOptions] - Sets optional behavior for the token grant
     * @param {ActorParams} [options.actor] - Optional actor parameters for creating annotator tokens
     * @returns {Promise<TokenInfo>} Promise resolving to the new token info
     */
    exchangeToken(scopes, resource, options) {
        return this.getAccessToken(options).then((accessToken) => this._tokenManager.exchangeToken(accessToken, scopes, resource, options));
    }
    /**
     * Handle an an "Expired Tokens" Error. If our tokens are expired, we need to clear the token
     * store (if present) before continuing.
     *
     * @param {Errors~ExpiredTokensError} err An "expired tokens" error including information
     *  about the request/response.
     * @returns {Promise<Error>} Promise resolving to an error.  This will
     *  usually be the original response error, but could an error from trying to access the
     *  token store as well.
     */
    handleExpiredTokensError(err /* FIXME */) {
        if (!this._tokenStore) {
            return bluebird_1.Promise.resolve(err);
        }
        // If a token store is available, clear the store and throw either error
        // eslint-disable-next-line promise/no-promise-in-callback
        return this._tokenStore
            .clearAsync()
            .catch((e) => errors_1.default.unwrapAndThrow(e))
            .then(() => {
            throw err;
        });
    }
}
/**
 * @module box-node-sdk/lib/sessions/app-auth-session
 * @see {@Link AppAuthSession}
 */
exports.default = AppAuthSession;
//# sourceMappingURL=app-auth-session.js.map