net_urlHelper.js

import Configuration from "../util/configuration.js";
import Environment from "./environment.js";

/**
 * A utility class for helping to compose SolarNet URLs for the REST API.
 *
 * This class is essentially abstract and meant to have mixin helper objects extend it.
 * @alias module:net~UrlHelper
 */
class UrlHelper {
	/**
	 * Constructor.
	 *
	 * @param {module:net~Environment|object} [environment] the optional initial environment to use;
	 *        if a non-`Environment` object is passed then the properties of that object will
	 *        be used to construct a new `Environment` instance
	 */
	constructor(environment) {
		let env = environment instanceof Environment ? environment : new Environment(environment);

		/**
		 * The environment associated with this helper.
		 * @member {module:net~Environment}
		 */
		this.environment = env;

		this._parameters = new Configuration();
	}

	/**
	 * Get a parameters object that can be used to hold URL variables.
	 *
	 * @readonly
	 * @type {module:util~Configuration}
	 */
	get parameters() {
		return this._parameters;
	}

	/**
	 * Get or set an environment parameter.
	 *
	 * This is a shortcut for calling {@link module:net~Configuration#value} on the
	 * `environment` object.
	 *
	 * @param {string} key the environment parameter name to get
	 * @param {object} [val] the optional value to set
	 * @returns {object} when called as a getter, the environment parameter value;
	 *                   when called as a setter, the environment parameters object
	 */
	env(...args) {
		return this.environment.value(...args);
	}

	/**
	 * Get or set a parameter.
	 *
	 * This is a shortcut for calling {@link module:net~Configuration#value} on the
	 * `parameters` object.
	 *
	 * @param {string} key the parameter name to get
	 * @param {Object} [val] the optional value to set
	 * @returns {Object} when called as a getter, the parameter value;
	 *                   when called as a setter, the parameters object
	 */
	parameter(...args) {
		return this._parameters.value(...args);
	}

	/**
	 * Get a URL for just the SolarNet host, without any path.
	 *
	 * This method constructs an absolute URL based on the following properties configured
	 * on this instance's {@link module:net~Environment}:
	 *
	 *  1. If {@link module:net~Environment#useTls environment.useTls()} returns `true` then
	 *     use HTTPS as the protocol, otherwise HTTP.
	 *  2. Use `host` for the host name or IP address, unless `proxyHost` is available.
	 *  3. Use `port` for the port, unless `proxyPort` is available. If neither are available, use `443` for
	 *     HTTPS or `80` for HTTP.
	 *
	 * @returns {string} the URL to the SolarNet host
	 */
	hostUrl() {
		const tls = this.environment.useTls();
		const host = this.environment.value("proxyHost") || this.environment.value("host");
		const port = +(this.environment.value("proxyPort") || this.environment.value("port"));
		let url = "http" + (tls ? "s" : "") + "://" + host;
		if ((tls && port > 0 && port !== 443) || (!tls && port > 0 && port !== 80)) {
			url += ":" + port;
		}
		return url;
	}

	/**
	 * Get a URL for just the SolarNet host using the WebSocket protocol, without any path.
	 *
	 * This method constructs an absolute URL based on the following properties configured
	 * on this instance's {@link module:net~Environment}:
	 *
	 *  1. If {@link module:net~Environment#useTls environment.useTls()} returns `true` then
	 *     use WSS as the protocol, otherwise WS.
	 *  2. Use `host` for the host name or IP address, unless `proxyHost` is available.
	 *  3. Use `port` for the port, unless `proxyPort` is available. If neither are available, use `443` for
	 *     WSS or `80` for WS.
	 *
	 * @returns {string} the URL to the SolarNet host WebSocket
	 */
	hostWebSocketUrl() {
		const tls = this.environment.useTls();
		const host = this.environment.value("proxyHost") || this.environment.value("host");
		const port = +(this.environment.value("proxyPort") || this.environment.value("port"));
		let url = "ws" + (tls ? "s" : "") + "://" + host;
		if ((tls && port > 0 && port !== 443) || (!tls && port > 0 && port !== 80)) {
			url += ":" + port;
		}
		return url;
	}

	/**
	 * Get the base URL to the REST API.
	 *
	 * This implementation is a stub, meant for subclasses to override. This implementation
	 * simply returns {@link module:net~UrlHelper#hostUrl}.
	 *
	 * @abstract
	 * @returns {string} the base URL to the REST API
	 */
	baseUrl() {
		return this.hostUrl();
	}

	/**
	 * Replace occurances of URL template variables with values from the `parameters`
	 * property and append to the host URL.
	 *
	 * This method provides a way to resolve an absolute URL based on the configured
	 * environment and parameters on this object.
	 *
	 * @param {string} template a URL path template
	 * @returns {string} an absolute URL
	 * @see module:net~UrlHelper#resolveTemplateUrl
	 */
	resolveTemplatePath(template) {
		return this.hostUrl() + this.resolveTemplateUrl(template);
	}

	/**
	 * Replace occurances of URL template variables with values from the `parameters`
	 * property.
	 *
	 * URL template variables are specified as `{<em>name</em>}`. The variable
	 * will be replaced by the value associated with property `name` in the
	 * `parameters` object. The value will be URI encoded.
	 *
	 * @param {string} template a URL template
	 * @returns {string} the URL with template variables resolved
	 */
	resolveTemplateUrl(template) {
		return UrlHelper.resolveTemplateUrl(template, this._parameters);
	}

	/**
	 * Replace occurances of URL template variables with values from a parameter object.
	 *
	 * URL template variables are specified as `{<em>name</em>}`. The variable
	 * will be replaced by the value associated with property `name` in the
	 * provided parameter object. The value will be URI encoded.
	 *
	 * @param {string} template a URL template
	 * @param {object} params an object whose properties should serve as template variables
	 * @returns {string} the URL
	 */
	static resolveTemplateUrl(template, params) {
		return template.replace(/\{([^}]+)\}/g, function (match, variableName) {
			let variableValue = params[variableName];
			return variableValue !== undefined ? encodeURIComponent(variableValue) : "";
		});
	}
}

export default UrlHelper;