rentdynamics
Version:
Package to help facilitate communicating with the Rent Dynamics API
237 lines • 31.7 kB
JavaScript
/**
* Client is a convenience class for interacting with the Rent Dynamics services.
*
* Note if this class does not suit your needs, it may be wise to roll your own implementation using
* the {@linkcode ClientHelpers} class.
*/
export class Client {
constructor(options) {
this.helpers = new ClientHelpers(options);
}
/**
* get wraps a request library to work with the Rent Dynamics API.
* @param endpoint the path following the baseUrl.
* @example get('/foo');
*/
async get(endpoint) {
const fullUrl = this.helpers.baseUrl + endpoint;
const headers = await this.helpers.getHeaders(endpoint, undefined, this.authToken);
return fetch(fullUrl.replace(/\|/g, '%7C'), {
method: 'GET',
headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' })
});
}
/**
* put wraps a request library to work with the Rent Dynamics API.
* @param endpoint the path following the baseUrl.
* @param payload a JSON serializable object.
* @example put('/foo', { bar: 1 });
*/
async put(endpoint, payload) {
const fullUrl = this.helpers.baseUrl + endpoint;
const headers = await this.helpers.getHeaders(endpoint, payload, this.authToken);
return fetch(fullUrl, {
method: 'PUT',
headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' }),
body: JSON.stringify(payload)
});
}
/**
* post wraps a request library to work with the Rent Dynamics API.
* @param endpoint the path following the baseUrl.
* @param payload a JSON serializable object.
* @example post('/foo', { bar: 1 });
*/
async post(endpoint, payload) {
const fullUrl = this.helpers.baseUrl + endpoint;
const headers = await this.helpers.getHeaders(endpoint, payload, this.authToken);
return fetch(fullUrl, {
method: 'POST',
headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' }),
body: JSON.stringify(payload)
});
}
/**
* delete wraps a request library to work with the Rent Dynamics API.
* @param endpoint the path following the baseUrl.
* @example delete('/foo/1');
*/
async delete(endpoint) {
const fullUrl = this.helpers.baseUrl + endpoint;
const headers = await this.helpers.getHeaders(endpoint, undefined, this.authToken);
return fetch(fullUrl, {
method: 'DELETE',
headers: Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' })
});
}
/**
* login enables an instance of {@linkcode Client} to make authenticated requests to the Rent
* Dynamics API.
*/
async login(username, password) {
const _password = await this.helpers.encryptPassword(password);
const endpoint = '/auth/login';
const result = await this.post(endpoint, { username, password: _password });
if (!result.ok) {
return result;
}
const { token } = await result.json();
this.authToken = token;
return result;
}
/** logout invalidates the users session generated by {@linkcode login}. */
async logout() {
const endpoint = '/auth/logout';
const result = await this.post(endpoint, { authToken: this.authToken });
if (!result.ok) {
return result;
}
this.authToken = undefined;
return result;
}
}
/** BASE_URL is a collection of base urls for each dev/prod Rent Dynamics service. */
export var BASE_URL;
(function (BASE_URL) {
BASE_URL["DEV_RD"] = "https://api.rentdynamics.dev";
BASE_URL["PROD_RD"] = "https://api.rentdynamics.com";
BASE_URL["DEV_RP"] = "https://api-dev.rentplus.com";
BASE_URL["PROD_RP"] = "https://api.rentplus.com";
})(BASE_URL || (BASE_URL = {}));
/** ClientOptions is consumed and updated by {@linkcode ClientHelpers}. */
export class ClientOptions {
constructor() {
/**
* baseUrl is the base request url. The default is the development rentdynamics api. A custom
* string may be provided beyond the {@linkcode BASE_URL} options.
*/
this.baseUrl = BASE_URL.DEV_RD;
/**
* getEncoder is used to encode text. The encoder can be overridden as needed. For example in a
* node environment.
* @example
* const options = new ClientOptions();
* options.getEncoder = async () => new (await import('util')).TextEncoder();
*/
this.getEncoder = async () => new TextEncoder();
/**
* getCryptographer is used for cryptography. The cryptographer can be overridden as needed. For
* example in a node environment.
* @example
* const options = new ClientOptions();
* options.getCryptographer = async () => (await import('crypto')).subtle;
*/
this.getCryptographer = async () => crypto.subtle;
}
}
/**
* ClientHelpers is a collection of utilities consumed by {@linkcode Client}. ClientHelpers can be
* used to calculate headers in case a consumer wants to build their own API client.
*/
export class ClientHelpers {
constructor(options) {
this.options = options;
}
/**
* baseUrl is the base url used throughout the {@linkcode ClientHelpers} instance. It is initially
* configured through {@linkcode ClientOptions}.
*/
get baseUrl() {
return this.options.baseUrl;
}
/**
* getTimestamp is used to calculate the timestamp header. This method is not likely to be called
* on it's own. Instead, it is typically used to mock the current time.
*/
getTimestamp() {
return Date.now();
}
/**
* getHeaders creates headers for the given params. If an auth token is included, this method will
* generate an `Authorization` header.
*/
async getHeaders(endpoint, payload, authToken) {
const headers = {};
if (this.options.apiKey && this.options.apiSecretKey) {
if (!payload || !Object.keys(payload).length) {
payload = undefined;
}
if (typeof payload !== 'undefined') {
payload = JSON.stringify(this.formatPayload(payload));
}
const timestamp = this.getTimestamp();
const nonce = await this.getNonce(timestamp, endpoint, payload);
if (authToken) {
headers.Authorization = 'TOKEN ' + authToken;
}
headers['x-rd-api-key'] = this.options.apiKey;
headers['x-rd-api-nonce'] = nonce;
headers['x-rd-timestamp'] = timestamp.toString();
return headers;
}
return headers;
}
/** formatPayload formats the payload for nonce calculation. */
formatPayload(payload) {
let formattedPayload = {};
if (payload === undefined || payload === null) {
formattedPayload = null;
}
else if (payload !== Object(payload)) {
formattedPayload = payload;
}
else if (Array.isArray(payload)) {
formattedPayload = [];
payload.forEach((_, index) => {
formattedPayload[index] = this.formatPayload(payload[index]);
});
}
else {
Object.keys(payload)
.sort()
.forEach(k => {
if (typeof payload[k] === 'object') {
formattedPayload[k] = this.formatPayload(payload[k]);
}
else if (typeof payload[k] === 'string') {
formattedPayload[k] = payload[k].replace(/ /g, '');
}
else {
formattedPayload[k] = payload[k];
}
});
}
return formattedPayload;
}
/** getNonce calculates the nonce for the given params. */
async getNonce(timestamp, url, payloadStr) {
if (!this.options.apiSecretKey)
return Promise.resolve('');
const encodedUrl = encodeURI(url)
.replace(/%7[Cc]/g, '|')
.replace(/%20/g, ' ');
const nonceStr = typeof payloadStr !== 'undefined'
? timestamp + encodedUrl + payloadStr
: timestamp + encodedUrl;
const cryptographer = await this.options.getCryptographer();
const encoder = await this.options.getEncoder();
const key = encoder.encode(this.options.apiSecretKey);
const data = encoder.encode(nonceStr);
const algorithm = { name: 'HMAC', hash: 'SHA-1' };
const hmac = await cryptographer.importKey('raw', key, algorithm, false, ['sign']);
const signed = await cryptographer.sign(algorithm.name, hmac, data);
return _hexDigest(signed);
}
/** encryptPassword encrypts the password for login. */
async encryptPassword(password) {
const cryptographer = await this.options.getCryptographer();
const encoder = await this.options.getEncoder();
const encodedPassword = encoder.encode(password);
const digestedPassword = await cryptographer.digest('SHA-1', encodedPassword);
return _hexDigest(digestedPassword);
}
}
const _hexDigest = (buf) => Array.from(new Uint8Array(buf))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBSUE7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8sTUFBTTtJQVFqQixZQUFZLE9BQXNCO1FBQ2hDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQWdCO1FBQy9CLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxHQUFHLFFBQVEsQ0FBQztRQUNoRCxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ25GLE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxFQUFFO1lBQzFDLE1BQU0sRUFBRSxLQUFLO1lBQ2IsT0FBTyxrQ0FDRixPQUFPLEtBQ1YsY0FBYyxFQUFFLGtCQUFrQixHQUNuQztTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBZ0IsRUFBRSxPQUFnQjtRQUNqRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUM7UUFDaEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNqRixPQUFPLEtBQUssQ0FBQyxPQUFPLEVBQUU7WUFDcEIsTUFBTSxFQUFFLEtBQUs7WUFDYixPQUFPLGtDQUNGLE9BQU8sS0FDVixjQUFjLEVBQUUsa0JBQWtC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QyxDQUFDO0tBQzVCLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztLQUN6QyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiogUGF5bG9hZCBpcyBrbm93biBhcyB0aGUgXCJib2R5XCIgb2YgYSBSZXF1ZXN0LiBUaGUgcGF5bG9hZCBtdXN0IGJlIEpTT04gc2VyaWFsaXphYmxlLiAqL1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbnR5cGUgUGF5bG9hZCA9IGFueTtcblxuLyoqXG4gKiBDbGllbnQgaXMgYSBjb252ZW5pZW5jZSBjbGFzcyBmb3IgaW50ZXJhY3Rpbmcgd2l0aCB0aGUgUmVudCBEeW5hbWljcyBzZXJ2aWNlcy5cbiAqXG4gKiBOb3RlIGlmIHRoaXMgY2xhc3MgZG9lcyBub3Qgc3VpdCB5b3VyIG5lZWRzLCBpdCBtYXkgYmUgd2lzZSB0byByb2xsIHlvdXIgb3duIGltcGxlbWVudGF0aW9uIHVzaW5nXG4gKiB0aGUge0BsaW5rY29kZSBDbGllbnRIZWxwZXJzfSBjbGFzcy5cbiAqL1xuZXhwb3J0IGNsYXNzIENsaWVudCB7XG4gIC8qKlxuICAgKiBhdXRoVG9rZW4gaWYgZGVmaW5lZCwgYXV0aFRva2VuIGlzIHVzZWQgdG8gbWFrZSBhdXRoZW50aWNhdGVkIHJlcXVlc3RzLiBOb3RlIGF1dGhUb2tlbiBpcyBub3RcbiAgICogdHlwaWNhbGx5IGdldC9zZXQgbWFudWFsbHksIGJ1dCBpbnN0ZWFkIGlzIG1hbmFnZWQgdGhyb3VnaCB0aGUgbG9naW4vbG9nb3V0IG1ldGhvZHMuXG4gICAqL1xuICBwdWJsaWMgYXV0aFRva2VuOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgaGVscGVyczogQ2xpZW50SGVscGVycztcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBDbGllbnRPcHRpb25zKSB7XG4gICAgdGhpcy5oZWxwZXJzID0gbmV3IENsaWVudEhlbHBlcnMob3B0aW9ucyk7XG4gIH1cblxuICAvKipcbiAgICogZ2V0IHdyYXBzIGEgcmVxdWVzdCBsaWJyYXJ5IHRvIHdvcmsgd2l0aCB0aGUgUmVudCBEeW5hbWljcyBBUEkuXG4gICAqIEBwYXJhbSBlbmRwb2ludCB0aGUgcGF0aCBmb2xsb3dpbmcgdGhlIGJhc2VVcmwuXG4gICAqIEBleGFtcGxlIGdldCgnL2ZvbycpO1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGdldChlbmRwb2ludDogc3RyaW5nKSB7XG4gICAgY29uc3QgZnVsbFVybCA9IHRoaXMuaGVscGVycy5iYXNlVXJsICsgZW5kcG9pbnQ7XG4gICAgY29uc3QgaGVhZGVycyA9IGF3YWl0IHRoaXMuaGVscGVycy5nZXRIZWFkZXJzKGVuZHBvaW50LCB1bmRlZmluZWQsIHRoaXMuYXV0aFRva2VuKTtcbiAgICByZXR1cm4gZmV0Y2goZnVsbFVybC5yZXBsYWNlKC9cXHwvZywgJyU3QycpLCB7XG4gICAgICBtZXRob2Q6ICdHRVQnLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAuLi5oZWFkZXJzLFxuICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogcHV0IHdyYXBzIGEgcmVxdWVzdCBsaWJyYXJ5IHRvIHdvcmsgd2l0aCB0aGUgUmVudCBEeW5hbWljcyBBUEkuXG4gICAqIEBwYXJhbSBlbmRwb2ludCB0aGUgcGF0aCBmb2xsb3dpbmcgdGhlIGJhc2VVcmwuXG4gICAqIEBwYXJhbSBwYXlsb2FkIGEgSlNPTiBzZXJpYWxpemFibGUgb2JqZWN0LlxuICAgKiBAZXhhbXBsZSBwdXQoJy9mb28nLCB7IGJhcjogMSB9KTtcbiAgICovXG4gIHB1YmxpYyBhc3luYyBwdXQoZW5kcG9pbnQ6IHN0cmluZywgcGF5bG9hZDogUGF5bG9hZCkge1xuICAgIGNvbnN0IGZ1bGxVcmwgPSB0aGlzLmhlbHBlcnMuYmFzZVVybCArIGVuZHBvaW50O1xuICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLmhlbHBlcnMuZ2V0SGVhZGVycyhlbmRwb2ludCwgcGF5bG9hZCwgdGhpcy5hdXRoVG9rZW4pO1xuICAgIHJldHVybiBmZXRjaChmdWxsVXJsLCB7XG4gICAgICBtZXRob2Q6ICdQVVQnLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAuLi5oZWFkZXJzLFxuICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICB9LFxuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkocGF5bG9hZClcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBwb3N0IHdyYXBzIGEgcmVxdWVzdCBsaWJyYXJ5IHRvIHdvcmsgd2l0aCB0aGUgUmVudCBEeW5hbWljcyBBUEkuXG4gICAqIEBwYXJhbSBlbmRwb2ludCB0aGUgcGF0aCBmb2xsb3dpbmcgdGhlIGJhc2VVcmwuXG4gICAqIEBwYXJhbSBwYXlsb2FkIGEgSlNPTiBzZXJpYWxpemFibGUgb2JqZWN0LlxuICAgKiBAZXhhbXBsZSBwb3N0KCcvZm9vJywgeyBiYXI6IDEgfSk7XG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcG9zdChlbmRwb2ludDogc3RyaW5nLCBwYXlsb2FkOiBQYXlsb2FkKSB7XG4gICAgY29uc3QgZnVsbFVybCA9IHRoaXMuaGVscGVycy5iYXNlVXJsICsgZW5kcG9pbnQ7XG4gICAgY29uc3QgaGVhZGVycyA9IGF3YWl0IHRoaXMuaGVscGVycy5nZXRIZWFkZXJzKGVuZHBvaW50LCBwYXlsb2FkLCB0aGlzLmF1dGhUb2tlbik7XG4gICAgcmV0dXJuIGZldGNoKGZ1bGxVcmwsIHtcbiAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICAuLi5oZWFkZXJzLFxuICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICB9LFxuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkocGF5bG9hZClcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkZWxldGUgd3JhcHMgYSByZXF1ZXN0IGxpYnJhcnkgdG8gd29yayB3aXRoIHRoZSBSZW50IER5bmFtaWNzIEFQSS5cbiAgICogQHBhcmFtIGVuZHBvaW50IHRoZSBwYXRoIGZvbGxvd2luZyB0aGUgYmFzZVVybC5cbiAgICogQGV4YW1wbGUgZGVsZXRlKCcvZm9vLzEnKTtcbiAgICovXG4gIHB1YmxpYyBhc3luYyBkZWxldGUoZW5kcG9pbnQ6IHN0cmluZykge1xuICAgIGNvbnN0IGZ1bGxVcmwgPSB0aGlzLmhlbHBlcnMuYmFzZVVybCArIGVuZHBvaW50O1xuICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLmhlbHBlcnMuZ2V0SGVhZGVycyhlbmRwb2ludCwgdW5kZWZpbmVkLCB0aGlzLmF1dGhUb2tlbik7XG4gICAgcmV0dXJuIGZldGNoKGZ1bGxVcmwsIHtcbiAgICAgIG1ldGhvZDogJ0RFTEVURScsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgIC4uLmhlYWRlcnMsXG4gICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbidcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBsb2dpbiBlbmFibGVzIGFuIGluc3RhbmNlIG9mIHtAbGlua2NvZGUgQ2xpZW50fSB0byBtYWtlIGF1dGhlbnRpY2F0ZWQgcmVxdWVzdHMgdG8gdGhlIFJlbnRcbiAgICogRHluYW1pY3MgQVBJLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGxvZ2luKHVzZXJuYW1lOiBzdHJpbmcsIHBhc3N3b3JkOiBzdHJpbmcpIHtcbiAgICBjb25zdCBfcGFzc3dvcmQgPSBhd2FpdCB0aGlzLmhlbHBlcnMuZW5jcnlwdFBhc3N3b3JkKHBhc3N3b3JkKTtcbiAgICBjb25zdCBlbmRwb2ludCA9ICcvYXV0aC9sb2dpbic7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5wb3N0KGVuZHBvaW50LCB7IHVzZXJuYW1lLCBwYXNzd29yZDogX3Bhc3N3b3JkIH0pO1xuICAgIGlmICghcmVzdWx0Lm9rKSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBjb25zdCB7IHRva2VuIH0gPSBhd2FpdCByZXN1bHQuanNvbigpO1xuICAgIHRoaXMuYXV0aFRva2VuID0gdG9rZW47XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKiBsb2dvdXQgaW52YWxpZGF0ZXMgdGhlIHVzZXJzIHNlc3Npb24gZ2VuZXJhdGVkIGJ5IHtAbGlua2NvZGUgbG9naW59LiAqL1xuICBwdWJsaWMgYXN5bmMgbG9nb3V0KCkge1xuICAgIGNvbnN0IGVuZHBvaW50ID0gJy9hdXRoL2xvZ291dCc7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5wb3N0KGVuZHBvaW50LCB7IGF1dGhUb2tlbjogdGhpcy5hdXRoVG9rZW4gfSk7XG4gICAgaWYgKCFyZXN1bHQub2spIHtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIHRoaXMuYXV0aFRva2VuID0gdW5kZWZpbmVkO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbn1cblxuLyoqIEJBU0VfVVJMIGlzIGEgY29sbGVjdGlvbiBvZiBiYXNlIHVybHMgZm9yIGVhY2ggZGV2L3Byb2QgUmVudCBEeW5hbWljcyBzZXJ2aWNlLiAqL1xuZXhwb3J0IGVudW0gQkFTRV9VUkwge1xuICBERVZfUkQgPSAnaHR0cHM6Ly9hcGkucmVudGR5bmFtaWNzLmRldicsXG4gIFBST0RfUkQgPSAnaHR0cHM6Ly9hcGkucmVudGR5bmFtaWNzLmNvbScsXG4gIERFVl9SUCA9ICdodHRwczovL2FwaS1kZXYucmVudHBsdXMuY29tJyxcbiAgUFJPRF9SUCA9ICdodHRwczovL2FwaS5yZW50cGx1cy5jb20nXG59XG5cbmV4cG9ydCB0eXBlIFJkRW5jb2RlciA9IFBpY2s8VGV4dEVuY29kZXIsICdlbmNvZGUnPjtcblxuZXhwb3J0IHR5cGUgUmRDcnlwdG9ncmFwaGVyID0gUGljazxTdWJ0bGVDcnlwdG8sICdpbXBvcnRLZXknPiAmXG4gIFBpY2s8U3VidGxlQ3J5cHRvLCAnc2lnbic+ICZcbiAgUGljazxTdWJ0bGVDcnlwdG8sICdkaWdlc3QnPjtcblxuLyoqIENsaWVudE9wdGlvbnMgaXMgY29uc3VtZWQgYW5kIHVwZGF0ZWQgYnkge0BsaW5rY29kZSBDbGllbnRIZWxwZXJzfS4gKi9cbmV4cG9ydCBjbGFzcyBDbGllbnRPcHRpb25zIHtcbiAgLyoqIGFwaUtleSBpZiBkZWZpbmVkIGFwaUtleSBpcyB1c2VkIHRvIGNhbGN1bGF0ZSBhdXRoIGhlYWRlcnMuICovXG4gIHB1YmxpYyBhcGlLZXk6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgLyoqIGFwaVNlY3JldEtleSBpZiBkZWZpbmVkIGFwaVNlY3JldEtleSBpcyB1c2VkIHRvIGNhbGN1bGF0ZSBhdXRoIGhlYWRlcnMuICAqL1xuICBwdWJsaWMgYXBpU2VjcmV0S2V5OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIC8qKlxuICAgKiBiYXNlVXJsIGlzIHRoZSBiYXNlIHJlcXVlc3QgdXJsLiBUaGUgZGVmYXVsdCBpcyB0aGUgZGV2ZWxvcG1lbnQgcmVudGR5bmFtaWNzIGFwaS4gQSBjdXN0b21cbiAgICogc3RyaW5nIG1heSBiZSBwcm92aWRlZCBiZXlvbmQgdGhlIHtAbGlua2NvZGUgQkFTRV9VUkx9IG9wdGlvbnMuXG4gICAqL1xuICBwdWJsaWMgYmFzZVVybDogQkFTRV9VUkwgfCBzdHJpbmcgPSBCQVNFX1VSTC5ERVZfUkQ7XG5cbiAgLyoqXG4gICAqIGdldEVuY29kZXIgaXMgdXNlZCB0byBlbmNvZGUgdGV4dC4gVGhlIGVuY29kZXIgY2FuIGJlIG92ZXJyaWRkZW4gYXMgbmVlZGVkLiBGb3IgZXhhbXBsZSBpbiBhXG4gICAqIG5vZGUgZW52aXJvbm1lbnQuXG4gICAqIEBleGFtcGxlXG4gICAqIGNvbnN0IG9wdGlvbnMgPSBuZXcgQ2xpZW50T3B0aW9ucygpO1xuICAgKiBvcHRpb25zLmdldEVuY29kZXIgPSBhc3luYyAoKSA9PiBuZXcgKGF3YWl0IGltcG9ydCgndXRpbCcpKS5UZXh0RW5jb2RlcigpO1xuICAgKi9cbiAgcHVibGljIGdldEVuY29kZXIgPSBhc3luYyAoKTogUHJvbWlzZTxSZEVuY29kZXI+ID0+IG5ldyBUZXh0RW5jb2RlcigpO1xuXG4gIC8qKlxuICAgKiBnZXRDcnlwdG9ncmFwaGVyIGlzIHVzZWQgZm9yIGNyeXB0b2dyYXBoeS4gVGhlIGNyeXB0b2dyYXBoZXIgY2FuIGJlIG92ZXJyaWRkZW4gYXMgbmVlZGVkLiBGb3JcbiAgICogZXhhbXBsZSBpbiBhIG5vZGUgZW52aXJvbm1lbnQuXG4gICAqIEBleGFtcGxlXG4gICAqIGNvbnN0IG9wdGlvbnMgPSBuZXcgQ2xpZW50T3B0aW9ucygpO1xuICAgKiBvcHRpb25zLmdldENyeXB0b2dyYXBoZXIgPSBhc3luYyAoKSA9PiAoYXdhaXQgaW1wb3J0KCdjcnlwdG8nKSkuc3VidGxlO1xuICAgKi9cbiAgcHVibGljIGdldENyeXB0b2dyYXBoZXIgPSBhc3luYyAoKTogUHJvbWlzZTxSZENyeXB0b2dyYXBoZXI+ID0+IGNyeXB0by5zdWJ0bGU7XG59XG5cbi8qKlxuICogQ2xpZW50SGVscGVycyBpcyBhIGNvbGxlY3Rpb24gb2YgdXRpbGl0aWVzIGNvbnN1bWVkIGJ5IHtAbGlua2NvZGUgQ2xpZW50fS4gQ2xpZW50SGVscGVycyBjYW4gYmVcbiAqIHVzZWQgdG8gY2FsY3VsYXRlIGhlYWRlcnMgaW4gY2FzZSBhIGNvbnN1bWVyIHdhbnRzIHRvIGJ1aWxkIHRoZWlyIG93biBBUEkgY2xpZW50LlxuICovXG5leHBvcnQgY2xhc3MgQ2xpZW50SGVscGVycyB7XG4gIHByaXZhdGUgb3B0aW9uczogQ2xpZW50T3B0aW9ucztcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBDbGllbnRPcHRpb25zKSB7XG4gICAgdGhpcy5vcHRpb25zID0gb3B0aW9ucztcbiAgfVxuXG4gIC8qKlxuICAgKiBiYXNlVXJsIGlzIHRoZSBiYXNlIHVybCB1c2VkIHRocm91Z2hvdXQgdGhlIHtAbGlua2NvZGUgQ2xpZW50SGVscGVyc30gaW5zdGFuY2UuIEl0IGlzIGluaXRpYWxseVxuICAgKiBjb25maWd1cmVkIHRocm91Z2gge0BsaW5rY29kZSBDbGllbnRPcHRpb25zfS5cbiAgICovXG4gIGdldCBiYXNlVXJsKCkge1xuICAgIHJldHVybiB0aGlzLm9wdGlvbnMuYmFzZVVybDtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXRUaW1lc3RhbXAgaXMgdXNlZCB0byBjYWxjdWxhdGUgdGhlIHRpbWVzdGFtcCBoZWFkZXIuIFRoaXMgbWV0aG9kIGlzIG5vdCBsaWtlbHkgdG8gYmUgY2FsbGVkXG4gICAqIG9uIGl0J3Mgb3duLiBJbnN0ZWFkLCBpdCBpcyB0eXBpY2FsbHkgdXNlZCB0byBtb2NrIHRoZSBjdXJyZW50IHRpbWUuXG4gICAqL1xuICBwdWJsaWMgZ2V0VGltZXN0YW1wKCkge1xuICAgIHJldHVybiBEYXRlLm5vdygpO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldEhlYWRlcnMgY3JlYXRlcyBoZWFkZXJzIGZvciB0aGUgZ2l2ZW4gcGFyYW1zLiBJZiBhbiBhdXRoIHRva2VuIGlzIGluY2x1ZGVkLCB0aGlzIG1ldGhvZCB3aWxsXG4gICAqIGdlbmVyYXRlIGFuIGBBdXRob3JpemF0aW9uYCBoZWFkZXIuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZ2V0SGVhZGVycyhcbiAgICBlbmRwb2ludDogc3RyaW5nLFxuICAgIHBheWxvYWQ/OiBQYXlsb2FkIHwgdW5kZWZpbmVkLFxuICAgIGF1dGhUb2tlbj86IHN0cmluZyB8IHVuZGVmaW5lZFxuICApIHtcbiAgICBjb25zdCBoZWFkZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgaWYgKHRoaXMub3B0aW9ucy5hcGlLZXkgJiYgdGhpcy5vcHRpb25zLmFwaVNlY3JldEtleSkge1xuICAgICAgaWYgKCFwYXlsb2FkIHx8ICFPYmplY3Qua2V5cyhwYXlsb2FkKS5sZW5ndGgpIHtcbiAgICAgICAgcGF5bG9hZCA9IHVuZGVmaW5lZDtcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgcGF5bG9hZCAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgcGF5bG9hZCA9IEpTT04uc3RyaW5naWZ5KHRoaXMuZm9ybWF0UGF5bG9hZChwYXlsb2FkKSk7XG4gICAgICB9XG4gICAgICBjb25zdCB0aW1lc3RhbXAgPSB0aGlzLmdldFRpbWVzdGFtcCgpO1xuICAgICAgY29uc3Qgbm9uY2UgPSBhd2FpdCB0aGlzLmdldE5vbmNlKHRpbWVzdGFtcCwgZW5kcG9pbnQsIHBheWxvYWQpO1xuICAgICAgaWYgKGF1dGhUb2tlbikge1xuICAgICAgICBoZWFkZXJzLkF1dGhvcml6YXRpb24gPSAnVE9LRU4gJyArIGF1dGhUb2tlbjtcbiAgICAgIH1cbiAgICAgIGhlYWRlcnNbJ3gtcmQtYXBpLWtleSddID0gdGhpcy5vcHRpb25zLmFwaUtleTtcbiAgICAgIGhlYWRlcnNbJ3gtcmQtYXBpLW5vbmNlJ10gPSBub25jZTtcbiAgICAgIGhlYWRlcnNbJ3gtcmQtdGltZXN0YW1wJ10gPSB0aW1lc3RhbXAudG9TdHJpbmcoKTtcbiAgICAgIHJldHVybiBoZWFkZXJzO1xuICAgIH1cbiAgICByZXR1cm4gaGVhZGVycztcbiAgfVxuXG4gIC8qKiBmb3JtYXRQYXlsb2FkIGZvcm1hdHMgdGhlIHBheWxvYWQgZm9yIG5vbmNlIGNhbGN1bGF0aW9uLiAqL1xuICBwdWJsaWMgZm9ybWF0UGF5bG9hZChwYXlsb2FkOiBQYXlsb2FkKTogUGF5bG9hZCB7XG4gICAgbGV0IGZvcm1hdHRlZFBheWxvYWQ6IFBheWxvYWQgPSB7fTtcbiAgICBpZiAocGF5bG9hZCA9PT0gdW5kZWZpbmVkIHx8IHBheWxvYWQgPT09IG51bGwpIHtcbiAgICAgIGZvcm1hdHRlZFBheWxvYWQgPSBudWxsO1xuICAgIH0gZWxzZSBpZiAocGF5bG9hZCAhPT0gT2JqZWN0KHBheWxvYWQpKSB7XG4gICAgICBmb3JtYXR0ZWRQYXlsb2FkID0gcGF5bG9hZDtcbiAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkocGF5bG9hZCkpIHtcbiAgICAgIGZvcm1hdHRlZFBheWxvYWQgPSBbXTtcbiAgICAgIHBheWxvYWQuZm9yRWFjaCgoXywgaW5kZXgpID0+IHtcbiAgICAgICAgZm9ybWF0dGVkUGF5bG9hZFtpbmRleF0gPSB0aGlzLmZvcm1hdFBheWxvYWQocGF5bG9hZFtpbmRleF0pO1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIE9iamVjdC5rZXlzKHBheWxvYWQpXG4gICAgICAgIC5zb3J0KClcbiAgICAgICAgLmZvckVhY2goayA9PiB7XG4gICAgICAgICAgaWYgKHR5cGVvZiBwYXlsb2FkW2tdID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgZm9ybWF0dGVkUGF5bG9hZFtrXSA9IHRoaXMuZm9ybWF0UGF5bG9hZChwYXlsb2FkW2tdKTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBwYXlsb2FkW2tdID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgZm9ybWF0dGVkUGF5bG9hZFtrXSA9IHBheWxvYWRba10ucmVwbGFjZSgvIC9nLCAnJyk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZvcm1hdHRlZFBheWxvYWRba10gPSBwYXlsb2FkW2tdO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBmb3JtYXR0ZWRQYXlsb2FkO1xuICB9XG5cbiAgLyoqIGdldE5vbmNlIGNhbGN1bGF0ZXMgdGhlIG5vbmNlIGZvciB0aGUgZ2l2ZW4gcGFyYW1zLiAqL1xuICBwdWJsaWMgYXN5bmMgZ2V0Tm9uY2UodGltZXN0YW1wOiBudW1iZXIsIHVybDogc3RyaW5nLCBwYXlsb2FkU3RyPzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBpZiAoIXRoaXMub3B0aW9ucy5hcGlTZWNyZXRLZXkpIHJldHVybiBQcm9taXNlLnJlc29sdmUoJycpO1xuICAgIGNvbnN0IGVuY29kZWRVcmwgPSBlbmNvZGVVUkkodXJsKVxuICAgICAgLnJlcGxhY2UoLyU3W0NjXS9nLCAnfCcpXG4gICAgICAucmVwbGFjZSgvJTIwL2csICcgJyk7XG4gICAgY29uc3Qgbm9uY2VTdHIgPVxuICAgICAgdHlwZW9mIHBheWxvYWRTdHIgIT09ICd1bmRlZmluZWQnXG4gICAgICAgID8gdGltZXN0YW1wICsgZW5jb2RlZFVybCArIHBheWxvYWRTdHJcbiAgICAgICAgOiB0aW1lc3RhbXAgKyBlbmNvZGVkVXJsO1xuICAgIGNvbnN0IGNyeXB0b2dyYXBoZXIgPSBhd2FpdCB0aGlzLm9wdGlvbnMuZ2V0Q3J5cHRvZ3JhcGhlcigpO1xuICAgIGNvbnN0IGVuY29kZXIgPSBhd2FpdCB0aGlzLm9wdGlvbnMuZ2V0RW5jb2RlcigpO1xuICAgIGNvbnN0IGtleSA9IGVuY29kZXIuZW5jb2RlKHRoaXMub3B0aW9ucy5hcGlTZWNyZXRLZXkpO1xuICAgIGNvbnN0IGRhdGEgPSBlbmNvZGVyLmVuY29kZShub25jZVN0cik7XG4gICAgY29uc3QgYWxnb3JpdGhtID0geyBuYW1lOiAnSE1BQycsIGhhc2g6ICdTSEEtMScgfTtcbiAgICBjb25zdCBobWFjID0gYXdhaXQgY3J5cHRvZ3JhcGhlci5pbXBvcnRLZXkoJ3JhdycsIGtleSwgYWxnb3JpdGhtLCBmYWxzZSwgWydzaWduJ10pO1xuICAgIGNvbnN0IHNpZ25lZCA9IGF3YWl0IGNyeXB0b2dyYXBoZXIuc2lnbihhbGdvcml0aG0ubmFtZSwgaG1hYywgZGF0YSk7XG4gICAgcmV0dXJuIF9oZXhEaWdlc3Qoc2lnbmVkKTtcbiAgfVxuXG4gIC8qKiBlbmNyeXB0UGFzc3dvcmQgZW5jcnlwdHMgdGhlIHBhc3N3b3JkIGZvciBsb2dpbi4gKi9cbiAgcHVibGljIGFzeW5jIGVuY3J5cHRQYXNzd29yZChwYXNzd29yZDogc3RyaW5nKSB7XG4gICAgY29uc3QgY3J5cHRvZ3JhcGhlciA9IGF3YWl0IHRoaXMub3B0aW9ucy5nZXRDcnlwdG9ncmFwaGVyKCk7XG4gICAgY29uc3QgZW5jb2RlciA9IGF3YWl0IHRoaXMub3B0aW9ucy5nZXRFbmNvZGVyKCk7XG4gICAgY29uc3QgZW5jb2RlZFBhc3N3b3JkID0gZW5jb2Rlci5lbmNvZGUocGFzc3dvcmQpO1xuICAgIGNvbnN0IGRpZ2VzdGVkUGFzc3dvcmQgPSBhd2FpdCBjcnlwdG9ncmFwaGVyLmRpZ2VzdCgnU0hBLTEnLCBlbmNvZGVkUGFzc3dvcmQpO1xuICAgIHJldHVybiBfaGV4RGlnZXN0KGRpZ2VzdGVkUGFzc3dvcmQpO1xuICB9XG59XG5cbmNvbnN0IF9oZXhEaWdlc3QgPSAoYnVmOiBBcnJheUJ1ZmZlcik6IHN0cmluZyA9PlxuICBBcnJheS5mcm9tKG5ldyBVaW50OEFycmF5KGJ1ZikpXG4gICAgLm1hcChiID0+IGIudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDIsICcwJykpXG4gICAgLmpvaW4oJycpO1xuIl19