"use strict";
/**
 * @fileoverview Configuration Object
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const https = __importStar(require("https"));
const stream_1 = require("stream");
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const merge_options_1 = __importDefault(require("merge-options"));
const packageJson = require('../../package.json');
const sdkVersion = packageJson.version;
// ------------------------------------------------------------------------------
// Private
// ------------------------------------------------------------------------------
const nodeVersion = process.version;
var defaults = {
    clientID: null,
    clientSecret: null,
    apiRootURL: 'https://api.box.com',
    uploadAPIRootURL: 'https://upload.box.com/api',
    authorizeRootURL: 'https://account.box.com/api',
    apiVersion: '2.0',
    uploadRequestTimeoutMS: 60000,
    retryIntervalMS: 2000,
    numMaxRetries: 5,
    retryStrategy: null,
    expiredBufferMS: 180000,
    appAuth: undefined,
    iterators: false,
    enterpriseID: undefined,
    analyticsClient: null,
    disableStreamPassThrough: false,
    proxy: {
        url: null,
        username: null,
        password: null,
    },
    request: {
        // By default, require API SSL cert to be valid
        strictSSL: true,
        // Use an agent with keep-alive enabled to avoid performing SSL handshake per connection
        agentClass: https.Agent,
        agentOptions: {
            keepAlive: true,
        },
        // Encode requests as JSON. Encode the response as well if JSON is returned.
        json: true,
        // Do not encode the response as a string, since the response could be a file. return Buffers instead.
        encoding: null,
        // A redirect is usually information we want to handle, so don't automatically follow
        followRedirect: false,
        // By default, we attach a version-specific user-agent string to SDK requests
        headers: {
            'User-Agent': `Box Node.js SDK v${sdkVersion} (Node ${nodeVersion})`,
        },
    },
};
var appAuthDefaults = {
    algorithm: 'RS256',
    expirationTime: 30,
    verifyTimestamp: false,
};
/**
 * Validate the basic Config values needed for the SDK to function
 * @param {UserConfigurationOptions} params The user-supplied config values
 * @returns {void}
 * @throws {AssertionError}
 * @private
 */
function validateBasicParams(params) {
    // Assert that the given params valid, and that required values are present
    (0, assert_1.default)(typeof params.clientID === 'string', '"clientID" must be set via init() before using the SDK.');
    (0, assert_1.default)(typeof params.clientSecret === 'string', '"clientSecret" must be set via init() before using the SDK.');
}
/**
 * Validate app auth-specific Config values
 * @param {Object} appAuth The user-supplied app auth values
 * @returns {void}
 * @throws {AssertionError}
 * @private
 */
function validateAppAuthParams(appAuth) {
    (0, assert_1.default)(typeof appAuth.keyID === 'string', 'Key ID must be provided in app auth params');
    (0, assert_1.default)(typeof appAuth.privateKey === 'string' ||
        appAuth.privateKey instanceof Buffer, 'Private key must be provided in app auth params');
    (0, assert_1.default)(typeof appAuth.passphrase === 'string' && appAuth.passphrase.length > 0, 'Passphrase must be provided in app auth params');
    var validAlgorithms = ['RS256', 'RS384', 'RS512'];
    if (typeof appAuth.algorithm !== 'undefined') {
        (0, assert_1.default)(validAlgorithms.indexOf(appAuth.algorithm) > -1, `Algorithm in app auth params must be one of: ${validAlgorithms.join(', ')}`);
    }
    if (typeof appAuth.expirationTime !== 'undefined') {
        (0, assert_1.default)(Number.isInteger(appAuth.expirationTime) &&
            appAuth.expirationTime > 0 &&
            appAuth.expirationTime <= 60, 'Valid token expiration time (0 - 60) must be provided in app auth params');
    }
}
/**
 * Update the agentClass based on the proxy config values passed in by the user
 * @param {UserConfigurationOptions} params The current Config values
 * @returns {void}
 * @private
 */
function updateRequestAgent(params) {
    if (params.proxy.url) {
        let proxyUrl = params.proxy.url;
        if (params.proxy.username && params.proxy.password) {
            proxyUrl = proxyUrl.replace('://', `://${params.proxy.username}:${params.proxy.password}@`);
        }
        const { ProxyAgent } = require('proxy-agent');
        params.request.agentClass = ProxyAgent;
        params.request.agentOptions = Object.assign({}, params.request.agentOptions, {
            getProxyForUrl: (url) => proxyUrl,
        });
    }
}
/**
 * Deep freeze an object and all nested objects within it. It doesn't go deep on
 * Buffers and Readable streams so can be used on objects containing requests.
 * @param {Object} obj The object to freeze
 * @returns {Object} The frozen object
 */
function deepFreezeWithRequest(obj) {
    Object.freeze(obj);
    Object.getOwnPropertyNames(obj).forEach(function (name) {
        const prop = obj[name];
        if (prop !== null &&
            typeof prop === 'object' &&
            obj.hasOwnProperty(name) &&
            !Object.isFrozen(prop) &&
            !(prop instanceof Buffer) &&
            !(prop instanceof stream_1.Readable)) {
            deepFreezeWithRequest(obj[name]);
        }
    });
    return obj;
}
// ------------------------------------------------------------------------------
// Public
// ------------------------------------------------------------------------------
/**
 * A Config Object holds the configuration options of the current setup. These are all
 * customizable by the user, and will default if no value is specified in the given params
 * object. The object is frozen on initialization, so that no values can be changed after
 * setup.
 *
 * @param {UserConfigurationOptions} params - The config options set by the user
 * @constructor
 */
class Config {
    constructor(params) {
        validateBasicParams(params);
        if (typeof params.appAuth === 'object') {
            validateAppAuthParams(params.appAuth);
            params.appAuth = (0, merge_options_1.default)({}, appAuthDefaults, params.appAuth);
        }
        // Ensure that we don't accidentally assign over Config methods
        (0, assert_1.default)(!params.hasOwnProperty('extend'), 'Config params may not override Config methods');
        (0, assert_1.default)(!params.hasOwnProperty('_params'), 'Config params may not override Config methods');
        // Set the given params or default value if params property is missing
        this._params = (0, merge_options_1.default)(defaults, params);
        updateRequestAgent(this._params);
        Object.assign(this, this._params);
        // Freeze the object so that configuration options cannot be modified
        deepFreezeWithRequest(this);
    }
    /**
     * Extend the current config into a new config with new params overriding old ones
     * @param {UserConfigurationOptions} params The override options
     * @returns {Config} The extended configuration
     */
    extend(params) {
        var newParams = (0, merge_options_1.default)({}, this._params, params);
        delete newParams.extend;
        delete newParams._params;
        return new Config(newParams);
    }
}
/**
 * @module box-node-sdk/lib/util/config
 * @see {@Link Config}
 */
exports.default = Config;
//# sourceMappingURL=config.js.map