peri-templating-api
Version:
The PERI Templating API client
552 lines (466 loc) • 17.7 kB
JavaScript
/**
* PERI Templating API (for Javascript) 0.1.0.
*
* Copyright (c) 2014-2021 Platforme International.
*
* This source code is licensed under the Apache 2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
var fs = require('fs');
var path = require('path');
var process$1 = require('process');
var fetch = require('node-fetch');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
/**
* Yonius 0.5.8.
*
* Copyright (c) 2008-2021 Hive Solutions Lda.
*
* This source code is licensed under the Apache 2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
class Observable {
constructor() {
this.callbacks = {};
}
bind(event, callback) {
const callbacks = this.callbacks[event] || [];
callbacks.push(callback);
this.callbacks[event] = callbacks;
return callback;
}
unbind(event, callback) {
const callbacks = this.callbacks[event] || [];
if (!callback) {
delete this.callbacks[event];
return;
}
const index = callbacks.indexOf(callback);
if (index === -1) {
return;
}
callbacks.splice(index, 1);
this.callbacks[event] = callbacks;
}
trigger(event) {
const callbacks = this.callbacks[event] || [];
const results = [];
for (const callback of callbacks) {
const result = callback.apply(this, Array.prototype.slice.call(arguments, 1));
result !== undefined && result !== null && results.push(result);
}
return Promise.all(results);
}
}
const verify = function(
condition,
message = null,
code = null,
exception = null,
kwargs = {}
) {
if (condition) return;
message = message || "Verification failed";
const Exception = exception || Error;
kwargs = Object.assign({}, kwargs);
if (message !== null && message !== undefined) kwargs.message = message;
if (code !== null && message !== undefined) kwargs.code = code;
const throwable = new Exception(kwargs.message || undefined);
throwable.kwargs = kwargs;
for (const [key, value] of Object.entries(kwargs)) {
if (throwable[key] !== undefined) continue;
throwable[key] = value;
}
throw throwable;
};
let HOME_DIR = null;
const pathExists = async function(path) {
try {
await fs.promises.access(path);
} catch (error) {
return false;
}
return true;
};
const expandUser = function(path$1) {
if (!path$1) return path$1;
if (path$1 === "~") return _homeDir();
if (path$1.slice(0, 2) !== "~/") return path$1;
return path.join(HOME_DIR, path$1.slice(2));
};
const getEnv = function(name) {
// eslint-disable-next-line no-undef
if (typeof Deno !== "undefined") return Deno.env.get(name);
return process$1.env[name];
};
const getEnvObject = function() {
// eslint-disable-next-line no-undef
if (typeof Deno !== "undefined") return Deno.env.toObject();
return process$1.env;
};
const _homeDir = function() {
if (HOME_DIR !== null) return HOME_DIR;
const isWindows = Boolean(typeof process !== "undefined" && process.platform === "win32");
HOME_DIR = getEnv(isWindows ? "USERPROFILE" : "HOME") || "/";
return HOME_DIR;
};
const FILE_NAME = "yonius.json";
const HOME_FILE = "~/.home";
const IMPORT_NAMES = ["$import", "$include", "$IMPORT", "$INCLUDE"];
const CASTS = {
int: v => (typeof v === "number" ? v : parseInt(v)),
float: v => (typeof v === "number" ? v : parseFloat(v)),
bool: v => (typeof v === "boolean" ? v : ["1", "true", "True"].includes(v)),
list: v => (Array.isArray(v) ? v : v.split(";")),
tuple: v => (Array.isArray(v) ? v : v.split(";"))
};
const globals =
typeof global === "undefined"
? typeof window === "undefined"
? typeof self === "undefined"
? {}
: self
: window
: global;
globals.CONFIGS = globals.CONFIGS === undefined ? {} : globals.CONFIGS;
globals.CONFIG_F = globals.CONFIG_F === undefined ? [] : globals.CONFIG_F;
globals.HOMES = globals.HOMES === undefined ? [] : globals.HOMES;
globals.LOADED = globals.LOADED === undefined ? false : globals.LOADED;
const conf = function(name, fallback = undefined, cast = null, ctx = null) {
const configs = ctx ? ctx.configs : globals.CONFIGS;
cast = _castR(cast);
let value = configs[name] === undefined ? fallback : configs[name];
if (cast && value !== undefined && value !== null) value = cast(value);
return value;
};
const load = async function(
names = [FILE_NAME],
path$1 = null,
encoding = "utf-8",
force = false,
ctx = null
) {
if (globals.LOADED && !force) return;
let paths = [];
const homes = await getHomes();
for (const home of homes) {
paths = paths.concat([path.join(home), path.join(home, ".config")]);
}
paths.push(path$1);
for (const path of paths) {
for (const name of names) {
await loadFile(name, path, encoding, ctx);
}
}
await loadEnv(ctx);
globals.LOADED = true;
};
const loadFile = async function(
name = FILE_NAME,
path$1 = null,
encoding = "utf-8",
ctx = null
) {
const configs = ctx ? ctx.configs : globals.CONFIGS;
const configF = ctx ? ctx.configF : globals.CONFIG_F;
let key;
let value;
let exists;
let filePath;
if (path$1) path$1 = path.normalize(path$1);
if (path$1) filePath = path.join(path$1, name);
else filePath = name;
filePath = path.resolve(filePath);
filePath = path.normalize(filePath);
const basePath = path.dirname(filePath);
exists = await pathExists(filePath);
if (!exists) return;
exists = configF.includes(filePath);
if (exists) configF.splice(configF.indexOf(filePath), 1);
configF.push(filePath);
const data = await fs.promises.readFile(filePath, { encoding: encoding });
const dataJ = JSON.parse(data);
await _loadIncludes(basePath, dataJ, encoding);
for ([key, value] of Object.entries(dataJ)) {
if (!_isValid(key)) continue;
configs[key] = value;
}
};
const loadEnv = async function(ctx = null) {
const env = getEnvObject();
const configs = ctx ? ctx.configs : globals.CONFIGS;
if (env === undefined || env === null) return;
Object.entries(env).forEach(function([key, value]) {
configs[key] = value;
});
};
const getHomes = async function(
filePath = HOME_FILE,
fallback = "~",
encoding = "utf-8",
forceDefault = false
) {
if (globals.HOMES.length > 0) return globals.HOMES;
const env = getEnvObject();
globals.HOMES = env.HOMES === undefined ? null : env.HOMES;
globals.HOMES = globals.HOMES ? globals.HOMES.split(";") : globals.HOMES;
if (globals.HOMES !== null) return globals.HOMES;
fallback = expandUser(fallback);
fallback = path.normalize(fallback);
globals.HOMES = [fallback];
filePath = expandUser(filePath);
filePath = path.normalize(filePath);
const exists = await pathExists(filePath);
if (!exists) return globals.HOMES;
if (!forceDefault) globals.HOMES.splice(0, globals.HOMES.length);
let data = await fs.promises.readFile(filePath, { encoding: encoding });
data = data.trim();
let paths = data.split(/\r?\n/);
paths = paths.map(v => v.trim());
for (let path$1 of paths) {
path$1 = path$1.trim();
if (!path$1) continue;
path$1 = expandUser(path$1);
path$1 = path.normalize(path$1);
globals.HOMES.push(path$1);
}
return globals.HOMES;
};
const _castR = function(cast) {
return CASTS[cast] === undefined ? cast : CASTS[cast];
};
const _loadIncludes = async function(basePath, config, encoding = "utf-8") {
let includes = [];
for (const alias of IMPORT_NAMES) {
includes = config[alias] === undefined ? includes : config[alias];
}
if (typeof includes === "string") {
includes = includes.split(";");
}
for (const include of includes) {
await loadFile(include, basePath, encoding);
}
};
const _isValid = function(key) {
if (IMPORT_NAMES.includes(key)) return false;
return true;
};
/**
* Encodes the multiple values as and encoded URI component, the
* values can be wither defined as an array (order is preserved)
* or as an object (where sequence order is not preserved).
*
* The value of each item can be either a primitive type or a sequence
* in case it's of sequence the values are going to be encoded as
* multiple parameters separated by the '&' character.
*
* @param {(Array|Object[])} values The values to be encoded as an
* URI component (like GET params).
* @returns {String} A string with the query encoded values.
*/
const urlEncode = function(values) {
// constructs the parts array that is going to
// store the multiple and values
const parts = [];
// in case the provided value is not an array
// then assumes it's an object and retrieve entries
if (!Array.isArray(values)) {
values = Object.entries(values);
}
// iterates over the complete set of pairs available
// from the key value pairs to be able to encode them
// properly, notice that the values themselves can be
// sequences allowing multiple repetition of key
values.forEach(([key, value]) => {
if (!Array.isArray(value)) {
value = [value];
}
const keyEncoded = encodeURIComponent(key);
value.forEach(_value => {
if (_value === undefined || _value === null) {
return;
}
const valueEncoded = encodeURIComponent(_value);
parts.push(`${keyEncoded}=${valueEncoded}`);
});
});
// joins the complete set of parts with the and
// separator and then returns the final string value
return parts.join("&");
};
const AUTH_ERRORS = [401, 403, 440, 499];
class API extends Observable {
constructor(kwargs = {}) {
super();
this.kwargs = kwargs;
}
async build(method, url, options = {}) {}
async authCallback(params, headers) {}
async get(url, options = {}) {
const result = await this.methodBasic("GET", url, options);
return result;
}
async post(url, options = {}) {
const result = await this.methodPayload("POST", url, options);
return result;
}
async put(url, options = {}) {
const result = await this.methodPayload("PUT", url, options);
return result;
}
async delete(url, options = {}) {
const result = await this.methodBasic("DELETE", url, options);
return result;
}
async patch(url, options = {}) {
const result = await this.methodPayload("PATCH", url, options);
return result;
}
async options(url, options = {}) {
const result = await this.methodBasic("OPTIONS", url, options);
return result;
}
async methodBasic(method, url, options = {}) {
options.params = options.params !== undefined ? options.params : {};
options.headers = options.headers !== undefined ? options.headers : {};
try {
return await this._methodBasic(method, url, options);
} catch (err) {
if (AUTH_ERRORS.includes(err.code)) {
await this.authCallback(options.params, options.headers);
return await this._methodBasic(method, url, options);
} else {
throw err;
}
}
}
async methodPayload(method, url, options = {}) {
options.params = options.params !== undefined ? options.params : {};
options.headers = options.headers !== undefined ? options.headers : {};
try {
return await this._methodPayload(method, url, options);
} catch (err) {
if (AUTH_ERRORS.includes(err.code)) {
await this.authCallback(options.params, options.headers);
return await this._methodPayload(method, url, options);
} else {
throw err;
}
}
}
async _methodBasic(method, url, options = {}) {
const params = options.params !== undefined ? options.params : {};
const headers = options.headers !== undefined ? options.headers : {};
const kwargs = options.kwargs !== undefined ? options.kwargs : {};
const handle = options.handle !== undefined ? options.handle : true;
await this.build(method, url, {
params: params,
headers: headers,
kwargs: kwargs
});
const query = urlEncode(params || {});
if (query) url += url.includes("?") ? "&" + query : "?" + query;
const response = await fetch__default['default'](url, {
method: method,
headers: headers || {}
});
const result = handle ? await this._handleResponse(response) : response;
return result;
}
async _methodPayload(method, url, options = {}) {
const params = options.params !== undefined ? options.params : {};
let headers = options.headers !== undefined ? options.headers : {};
let data = options.data !== undefined ? options.data : null;
const dataJ = options.dataJ !== undefined ? options.dataJ : null;
const dataM = options.dataM !== undefined ? options.dataM : null;
let mime = options.mime !== undefined ? options.mime : null;
const kwargs = options.kwargs !== undefined ? options.kwargs : {};
const handle = options.handle !== undefined ? options.handle : true;
await this.build(method, url, {
params: params,
headers: headers,
data: data,
dataJ: dataJ,
dataM: dataM,
mime: mime,
kwargs: kwargs
});
const query = urlEncode(params || {});
if (data !== null) {
if (query) url += url.includes("?") ? "&" + query : "?" + query;
} else if (dataJ !== null) {
data = JSON.stringify(dataJ);
if (query) url += url.includes("?") ? "&" + query : "?" + query;
mime = mime || "application/json";
} else if (dataM !== null) {
if (query) url += url.includes("?") ? "&" + query : "?" + query;
} else if (query) {
data = query;
mime = mime || "application/x-www-form-urlencoded";
}
headers = Object.assign({}, headers);
if (mime) headers["Content-Type"] = mime;
const response = await fetch__default['default'](url, {
method: method,
headers: headers || {},
body: data
});
const result = handle ? await this._handleResponse(response) : response;
return result;
}
async _handleResponse(response, errorMessage = "Problem in request") {
let result = null;
if (
response.headers.get("content-type") &&
response.headers.get("content-type").toLowerCase().startsWith("application/json")
) {
result = await response.json();
} else if (
response.headers.get("content-type") &&
response.headers.get("content-type").toLowerCase().startsWith("text/")
) {
result = await response.text();
} else {
result = await response.blob();
}
verify(response.ok, result.error || errorMessage, response.status || 500);
return result;
}
}
const load$1 = async function() {
await load();
};
const BASE_URL = "http://localhost:3000/";
class API$1 extends API {
constructor(kwargs = {}) {
super(kwargs);
this.baseUrl = conf("PERI_TEMPLATING_BASE_URL", BASE_URL);
this.token = conf("PERI_TEMPLATING_TOKEN", null);
this.baseUrl = kwargs.baseUrl === undefined ? this.baseUrl : kwargs.baseUrl;
this.token = kwargs.token === undefined ? this.token : kwargs.token;
}
static async load() {
await load$1();
}
async build(method, url, options = {}) {
await super.build(method, url, options);
options.headers = options.params !== undefined ? options.headers : {};
options.kwargs = options.kwargs !== undefined ? options.kwargs : {};
const auth = options.kwargs.auth === undefined ? true : options.kwargs.auth;
delete options.kwargs.auth;
if (auth) {
options.headers.Authorization = `Bearer ${this.token}`;
}
}
async render(template, payload) {
const url = this.baseUrl + template;
return await this.post(url, {
dataJ: payload
});
}
}
exports.API = API$1;
//# sourceMappingURL=peri-templating-api.cjs.js.map