/**
* Simplicite(R) platform Javascript API client module (for node.js and browser).
* @module simplicite
* @version 3.0.3
* @license Apache-2.0
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Buffer } from 'buffer'; // Browser polyfill for Buffer
/**
* Constants
* @constant
*/
const constants = {
/**
* API client module version
* @constant {string}
*/
MODULE_VERSION: '3.0.3',
/**
* Default row ID field name
* @constant {string}
*/
DEFAULT_ROW_ID_NAME: 'row_id',
/**
* Default row ID value
* @constant {string}
*/
DEFAULT_ROW_ID: '0',
/**
* Default context
* @constant {number}
*/
CONTEXT_NONE: 0,
/**
* Search context
* @constant {number}
*/
CONTEXT_SEARCH: 1,
/**
* List context
* @constant {number}
*/
CONTEXT_LIST: 2,
/**
* Creation context
* @constant {number}
*/
CONTEXT_CREATE: 3,
/**
* Copy context
* @constant {number}
*/
CONTEXT_COPY: 4,
/**
* Update context
* @constant {number}
*/
CONTEXT_UPDATE: 5,
/**
* Delete context
* @constant {number}
*/
CONTEXT_DELETE: 6,
/**
* Chart context
* @constant {number}
*/
CONTEXT_GRAPH: 7,
/**
* Pivot table context
* @constant {number}
*/
CONTEXT_CROSSTAB: 8,
/**
* Publication context
* @constant {number}
*/
CONTEXT_PRINTTMPL: 9,
/**
* Bulk update context
* @constant {number}
*/
CONTEXT_UPDATEALL: 10,
/**
* Reference selection context
* @constant {number}
*/
CONTEXT_REFSELECT: 11,
/**
* Datamap selection context
* @constant {number}
*/
CONTEXT_DATAMAPSELECT: 12,
/**
* Pre validation context
* @constant {number}
*/
CONTEXT_PREVALIDATE: 13,
/**
* Post validation context
* @constant {number}
*/
CONTEXT_POSTVALIDATE: 14,
/**
* State transition context
* @constant {number}
*/
CONTEXT_STATETRANSITION: 15,
/**
* Export context
* @constant {number}
*/
CONTEXT_EXPORT: 16,
/**
* Import context
* @constant {number}
*/
CONTEXT_IMPORT: 17,
/**
* Association context
* @constant {number}
*/
CONTEXT_ASSOCIATE: 18,
/**
* Panel list context
* @constant {number}
*/
CONTEXT_PANELLIST: 19,
/**
* Action context
* @constant {number}
*/
CONTEXT_ACTION: 20,
/**
* Agenda context
* @constant {number}
*/
CONTEXT_AGENDA: 21,
/**
* Place map context
* @constant {number}
*/
CONTEXT_PLACEMAP: 22,
/**
* Foreign key (reference) type
* @constant {number}
*/
TYPE_ID: 0,
/**
* Integer type
* @constant {number}
*/
TYPE_INT: 1,
/**
* Decimal type
* @constant {number}
*/
TYPE_FLOAT: 2,
/**
* Short string type
* @constant {number}
*/
TYPE_STRING: 3,
/**
* Date type
* @constant {number}
*/
TYPE_DATE: 4,
/**
* Date and time type
* @constant {number}
*/
TYPE_DATETIME: 5,
/**
* Time type
* @constant {number}
*/
TYPE_TIME: 6,
/**
* Simple enumeration type
* @constant {number}
*/
TYPE_ENUM: 7,
/**
* Boolean type
* @constant {number}
*/
TYPE_BOOLEAN: 8,
/**
* Password type
* @constant {number}
*/
TYPE_PASSWORD: 9,
/**
* URL type
* @constant {number}
*/
TYPE_URL: 10,
/**
* HTML content type
* @constant {number}
*/
TYPE_HTML: 11,
/**
* Email type
* @constant {number}
*/
TYPE_EMAIL: 12,
/**
* Long string type
* @constant {number}
*/
TYPE_LONG_STRING: 13,
/**
* Multiple enumeration type
* @constant {number}
*/
TYPE_ENUM_MULTI: 14,
/**
* Validated string type
* @constant {number}
*/
TYPE_REGEXP: 15,
/**
* Document type
* @constant {number}
*/
TYPE_DOC: 17,
/**
* Decimal type
* @constant {number}
* @deprecated
*/
TYPE_FLOAT_EMPTY: 18,
/**
* External file type
* @constant {number}
* @deprecated
*/
TYPE_EXTFILE: 19,
/**
* Image type
* @constant {number}
*/
TYPE_IMAGE: 20,
/**
* Notepad type
* @constant {number}
*/
TYPE_NOTEPAD: 21,
/**
* Phone number type
* @constant {number}
*/
TYPE_PHONENUM: 22,
/**
* RGB color type
* @constant {number}
*/
TYPE_COLOR: 23,
/**
* Object type
* @constant {number}
*/
TYPE_OBJECT: 24,
/**
* Geocoordinates type
* @constant {number}
*/
TYPE_GEOCOORDS: 25,
/**
* Big decimal
* @constant {number}
*/
TYPE_BIGDECIMAL: 26,
/**
* Types strings
* @constant {Array}
*/
TYPES: [
'ID', //.0
'integer', // 1
'decimal', // 2
'string', // 3
'date', // 4
'datetime', // 5
'time', // 6
'enum', // 7
'boolean', // 8
'password', // 9
'url', // 10
'html', // 11
'email', // 12
'text', // 13
'multienum', // 14
'regexp', // 15
'undefined', // 16
'document', // 17
'simpledecimal', // 18
'extfile', // 19
'picture', // 20
'notepad', // 21
'phonenum', // 22
'color', // 23
'object', // 24
'geocoords', // 25
'bigdecimal' // 26
],
/**
* Not visible
* @constant {number}
*/
VIS_NOT: 0,
/**
* Hiiden (same as not visible)
* @constant {number}
*/
VIS_HIDDEN: 0,
/**
* Visible on lists only
* @constant {number}
*/
VIS_LIST: 1,
/**
* Visible on forms only
* @constant {number}
*/
VIS_FORM: 2,
/**
* Visible on both lists and forms only
* @constant {number}
*/
VIS_BOTH: 3,
/**
* No search
* @constant {number}
*/
SEARCH_NONE: 0,
/**
* Simple search
* @constant {number}
*/
SEARCH_MONO: 1,
/**
* Multiple search (checkboxes)
* @constant {number}
*/
SEARCH_MULTI_CHECK: 2,
/**
* Multiple search (listbox)
* @constant {number}
*/
SEARCH_MULTI_LIST: 3,
/**
* Search by period (date/time)
* @constant {number}
*/
SEARCH_PERIOD: 4,
/**
* True
* @constant {string}
*/
TRUE: '1',
/**
* False
* @constant {string}
*/
FALSE: '0',
/**
* Fatal error level
* @constant {number}
*/
ERRLEVEL_FATAL: 1,
/**
* Error level
* @constant {number}
*/
ERRLEVEL_ERROR: 2,
/**
* Warning level
* @constant {number}
*/
ERRLEVEL_WARNING: 3,
/**
* Image resource type
* @constant {number}
*/
RESOURCE_TYPE_IMAGE: 'IMG',
/**
* Icon resource type
* @constant {number}
*/
RESOURCE_TYPE_ICON: 'ICO',
/**
* Stylesheet resource type
* @constant {number}
*/
RESOURCE_TYPE_STYLESHEET: 'CSS',
/**
* Javascript resource type
* @constant {number}
*/
RESOURCE_TYPE_JAVASCRIPT: 'JS',
/**
* Default authentication header
* @constant {string}
*/
DEFAULT_AUTH_HEADER: 'authorization',
/**
* Simplicite authentication header
* @constant {string}
*/
SIMPLICITE_AUTH_HEADER: 'x-simplicite-authorization'
};
/**
* Simplicite application session. Same as <code>new Session(parameter)</code>.
* @param {object} params Parameters (see session class for details)
* @return {Session} session
*/
const session = (params) => {
return new Session(params);
};
/**
* Simplicite application session.
* @param {object} params Parameters
* @param {string} params.url Base URL of the Simplicite application
* @param {string} params.scheme URL scheme (e.g. <code>'https'</code>) of the Simplicite application (not needed if <code>url</code> is set)
* @param {string} params.host Hostname or IP address (e.g. <code>'myhost.mydomain.com'</code>) of the Simplicite application (not needed if <code>url</code> is set)
* @param {number} params.port Port (e.g. <code>443</code>) of the Simplicite application (not needed if <code>url</code> is set)
* @param {string} params.root Root context URL (e.g. <code>'/myapp'</code>) the Simplicite application (not needed if <code>url</code> is set)
* @param {boolean} [params.endpoint='api'] Endpoint (<code>'api'|'ui'|'uipublic'</code>)
* @param {string} [params.username] Username (not needed for the public UI endpoint)
* @param {string} [params.password] Password (not needed for the public UI endpoint)
* @param {string} [params.authtoken] Authentication token (if set, username and password are not needed; not needed for the public UI endpoint)
* @param {string} [params.authheader] Authorization HTTP header name (defaults to the standard <code>Authorization</code>, the alternative is the value of the <code>SIMPLICITE_AUTH_HEADER</code> constant, not needed for public endpoint)
* @param {string} [params.ajaxkey] Ajax key (only usefull for usage from the generic UI)
* @param {boolean} [params.debug=false] Debug mode?
* @param {function} [params.debugHandler] Debug handler function
* @param {function} [params.infoHandler] Info handler function
* @param {function} [params.warningHandler] Warning handler function
* @param {function} [params.errorHandler] Error handler function
* @param {function} [params.logHandler] Log handler function
* @class
*/
class Session {
/**
* Constructor
* @param params {object} Parameters
*/
constructor(params) {
/**
* Constants
* @member
*/
this.constants = constants;
/**
* Set username
* @param {string} usr Username
* @function
*/
this.setUsername = (usr) => {
this.username = usr;
};
/**
* Set password
* @param {string} pwd Password
* @function
*/
this.setPassword = (pwd) => {
this.password = pwd;
};
/**
* Set auth token
* @param {string} token Auth token
* @function
*/
this.setAuthToken = (token) => {
this.authtoken = token;
};
/**
* Set auth token expiry date
* @param {Date} expiry Auth token expiry
* @function
*/
this.setAuthTokenExpiryDate = (expiry) => {
this.authtokenexpiry = expiry;
};
/**
* Is the auth token expired?
* @return {boolean} true if the auth token is expired
* @function
*/
this.isAuthTokenExpired = () => {
return this.authtokenexpiry ? new Date() > this.authtokenexpiry : false;
};
/**
* Set Ajax key
* @param {string} key Ajax key
* @function
*/
this.setAjaxKey = (key) => {
this.ajaxkey = key;
};
/**
* Get business object cache key
* @param {string} name Business object name
* @param {string} [instance] Business object instance name, defaults to <code>js_<object name></code>
* @return {object} Business object cache key
* @function
*/
this.getBusinessObjectCacheKey = (name, instance) => {
return name + ':' + (instance || 'js_' + name);
};
/**
* Clears all data (credentials, objects, ...)
* @function
*/
this.clear = () => {
this.username = undefined;
this.password = undefined;
this.authtoken = undefined;
this.authtokenexpiry = undefined;
this.sessionid = undefined;
this.grant = undefined;
this.appinfo = undefined;
this.sysinfo = undefined;
this.devinfo = undefined;
this.businessObjectCache = new Map();
};
/**
* Basic HTTP authorization header value
* @return {string} HTTP authorization header value
* @function
*/
this.getBasicAuthHeader = () => {
return this.username && this.password
? 'Basic ' + Buffer.from(this.username + ':' + this.password).toString('base64')
: undefined;
};
/**
* Get bearer token header value
* @return {string} Bearer token header value
* @function
*/
this.getBearerTokenHeader = () => {
return this.authtoken
? 'Bearer ' + this.authtoken
: undefined;
};
/**
* Get error object
* @param {(string|object)} err Error
* @param {string} err.message Error message
* @param {number} [status] Optional error status (defaults to 200)
* @param {string} [origin] Optional error origin
* @return {object} Error object
* @function
*/
this.getError = (err, status, origin) => {
if (typeof err === 'string') { // plain text error
return { message: err, status: status || 200, origin };
}
else if (err.response) { // wrapped error
if (typeof err.response === 'string') {
return { message: err.response, status: status || 200, origin };
}
else {
if (origin)
try {
err.response.origin = origin;
}
catch (e) { /* ignore */ }
return err.response;
}
}
else { // other cases
if (origin)
try {
err.origin = origin;
}
catch (e) { /* ignore */ }
return err;
}
};
/**
* Compress data as blob
* @param data {string|any} Data to compress
* @return {Promise<Blob>} Promise to the compressed data blob
*/
this.compressData = (data) => {
const s = typeof data === 'string'
? new Blob([data], { type: 'text/plian' }).stream()
: new Blob([JSON.stringify(data)], { type: 'application/json' }).stream();
const cs = s.pipeThrough(new CompressionStream('gzip'));
return new Response(cs).blob();
};
/**
* Uncompress blob
* @param blob {Blob} Compressed data blob
* @return {Promise<string>} Promise to the uncompressed string
*/
this.uncompressData = (blob) => {
const us = blob.stream().pipeThrough(new DecompressionStream('gzip'));
return new Response(us).text();
};
/**
* Send request
* @param {string} path Path
* @param {object} [data] Data
* @param {function} [callback] Callback
* @param {function} [errorHandler] Error handler
* @function
*/
this.sendRequest = (path, data, callback, errorHandler) => {
const origin = 'Session.sendRequest';
const m = data ? 'POST' : 'GET';
const h = {};
if (data)
h['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8';
h.accept = 'application/json';
let b = this.getBearerTokenHeader();
if (b) {
h[this.authheader] = b;
}
else {
b = this.getBasicAuthHeader();
if (b)
h[this.authheader] = b;
}
let u = this.parameters.url + (path || '/');
if (this.ajaxkey)
u += (u.indexOf('?') >= 0 ? '&' : '?') + '_ajaxkey=' + encodeURIComponent(this.ajaxkey);
const d = data ? (typeof data === 'string' ? data : JSON.stringify(data)) : undefined;
this.debug(`[${origin}] ${m} ${u}${d ? ' with ' + d : ''}`);
fetch(u, {
method: m,
headers: h,
//compress: this.parameters.compress,
signal: AbortSignal.timeout(this.parameters.timeout),
body: d
}).then((res) => {
if (callback) {
res.text().then((textData) => {
callback.call(this, textData, res.status, res.headers);
});
}
}).catch((err) => {
const s = err.response && err.response.status ? err.response.status : undefined;
const e = err.response && err.response.data ? err.response.data : err;
if (errorHandler)
errorHandler.call(this, this.getError(e, s, origin));
else
throw e;
});
};
/**
* Parse response
* @param {object} res Response to parse
* @param {number} [status=200] HTTP status
* @return {object} Error object
* @function
*/
this.parseResponse = (res, status) => {
try {
if (status !== 200)
return { type: 'error', response: this.getError('HTTP status: ' + status, status) };
return typeof res === 'object' ? res : JSON.parse(res);
}
catch (e) {
return { type: 'error', response: this.getError('Parsing error: ' + e.message, status) };
}
};
/**
* Get health check (no need to be authenticated)
* @param {object} [opts] Options
* @param {boolean} [opts.full=false] Full health check?
* @param {function} [opts.error] Error handler function
* @return {promise<object>} Promise to the health data
* @function
*/
this.getHealth = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.getHealth';
opts = opts || {};
return new Promise((resolve, reject) => {
let p = `&full=${!!opts.full}`;
if (opts.businessCase)
p += `&_bc=${encodeURIComponent(opts.businessCase)}`;
this.sendRequest(`${this.parameters.healthpath}${p}`, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${res}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
resolve.call(this, r);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Alias to getHealth
* @param {object} [opts] Options
* @param {boolean} [opts.full=false] Full health check?
* @param {function} [opts.error] Error handler function
* @return {promise<object>} Promise to the health data
* @function
*/
this.health = this.getHealth;
/**
* Login
* @param {object} [opts] Options
* @param {string} [opts.username] Username (exclusive with authentication token)
* @param {string} [opts.password] Password (required if username is set)
* @param {string} [opts.authtoken] Authentication token ((exclusive with username)
* @param {function} [opts.error] Error handler function
* @return {promise<object>} Promise to the login result
* @function
*/
this.login = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.login';
opts = opts || {};
return new Promise((resolve, reject) => {
if ((opts.username || opts.login) && (opts.password || opts.pwd)) {
this.clear();
this.username = opts.username || opts.login;
this.password = opts.password || opts.pwd;
}
else if (opts.authtoken || opts.authToken || opts.token) {
this.clear();
this.authtoken = opts.authtoken || opts.authToken || opts.token;
}
this.sendRequest(this.parameters.loginpath, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type || (r.error ? 'error' : 'login')}`);
if (r.type === 'error' || r.error) {
const err = this.getError(r.response ? r.response : r, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
this.sessionid = r.response ? r.response.id : r.sessionid;
this.debug(`[${origin}] Session ID = ${this.sessionid}`);
this.username = r.response ? r.response.login : r.login;
if (this.username)
this.debug(`[${origin}] Username = ${this.username}`);
this.authtoken = r.response ? r.response.authtoken : r.authtoken;
if (this.authtoken)
this.debug(`[${origin}] Auth token = ${this.authtoken}`);
try {
const exp = new Date();
exp.setTime(r.response ? r.response.authtokenexpiry : r.authtokenexpiry);
this.authtokenexpiry = exp;
}
catch (e) {
this.authtokenexpiry = undefined;
}
if (this.authtokenexpiry)
this.debug(`[${origin}] Auth token expiry date = ${this.authtokenexpiry.toLocaleDateString()} ${this.authtokenexpiry.toLocaleTimeString()}`);
// Minimal grant from session data
this.grant = new Grant({
login: this.username,
userid: r.response ? r.response.userid : r.userid,
firstname: r.response ? r.response.firstname : r.firstname,
lastname: r.response ? r.response.lastname : r.lastname,
email: r.response ? r.response.email : r.email
});
resolve.call(this, r.response || r);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Logout
* @param {function} callback Callback (called upon success)
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @return {promise<object>} Promise to the logout result
* @function
*/
this.logout = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.logout';
opts = opts || {};
return new Promise((resolve, reject) => {
this.sendRequest(this.parameters.logoutpath, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type || (r.error ? 'error' : 'logout')}`);
if (r.type === 'error') {
const err = this.getError(r.response ? r.response : r, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
this.clear();
// Restore session parameter-level credentials if present
this.username = this.parameters.username;
this.password = this.parameters.password;
this.authtoken = this.parameters.authtoken;
resolve.call(this, r.response || r);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (err.status === 401) // Removes (expired or deleted) token if any
this.authtoken = undefined;
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get grant (current user data)
* @param {object} [opts] Options
* @param {boolean} [opts.inlinePicture=false] Inline user picture?
* @param {boolean} [opts.includeTexts=false] Include texts?
* @param {boolean} [opts.includeSysparams=false] Include system parameters?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<Grant>} A promise to the grant (also available as the <code>grant</code> member)
* @function
*/
this.getGrant = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.getGrant';
opts = opts || {};
return new Promise((resolve, reject) => {
let p = '&web=true'; // Required to be able to include texts
const txt = !!opts.includeTexts || !!opts.texts; // naming flexibility
p += `&texts=${encodeURIComponent(txt)}`;
const pic = !!opts.inlinePicture || !!opts.picture; // naming flexibility
if (pic)
p += '&inline_picture=true';
const sys = !!opts.includeSysparams || !!opts.sysparams; // naming flexibility
if (sys)
p += '&sysparams=true';
this.sendRequest(`${this.getPath('getgrant', opts)}${p}`, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
this.grant = new Grant(r.response); // Set as Grant
if (pic)
this.grant.picture = new Doc(this.grant.picture); // Set picture as Document
if (txt)
this.grant.texts = Object.assign(new Map(), this.grant.texts); // Set texts as Map
resolve.call(this, this.grant);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Change password
* @param {string} pwd Password
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} A promise to the change password result
* @function
*/
this.changePassword = (pwd, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.changePassword';
opts = opts || {};
return new Promise((resolve, reject) => {
this.sendRequest(`${this.getPath('setpassword', opts)}&password=${encodeURIComponent(pwd)}`, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
resolve.call(this, r.response);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get application info
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} A promise to the application info (also avialable as the <code>appinfo</code> member)
* @function
*/
this.getAppInfo = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.getAppInfo';
opts = opts || {};
return new Promise((resolve, reject) => {
this.sendRequest(this.getPath('getinfo', opts), undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
this.appinfo = r.response;
resolve.call(this, this.appinfo);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get system info
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} A promise to the system info (also avialable as the <code>sysinfo</code> member)
* @function
*/
this.getSysInfo = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.getSysInfo';
opts = opts || {};
return new Promise((resolve, reject) => {
this.sendRequest(this.getPath('sysinfo', opts), undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
this.sysinfo = r.response;
resolve.call(this, this.sysinfo);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get development info
* @param {string} [module] Module name
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} A promise to the develoment info (also avialable as the <code>devinfo</code> member)
* @function
*/
this.getDevInfo = (module, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.getDevInfo';
opts = opts || {};
return new Promise((resolve, reject) => {
let p = '';
if (module)
p += `&module=${encodeURIComponent(module)}`;
this.sendRequest(`${this.getPath('devinfo', opts)}${p}`, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
if (!module)
this.devinfo = r.response;
resolve.call(this, r.response);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get news
* @param {object} [opts] Options
* @param {boolean} [opts.inlineImages=false] Inline news images?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<array>} A promise to the list of news (also avialable as the <code>news</code> member)
* @function
*/
this.getNews = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.getNews';
opts = opts || {};
return new Promise((resolve, reject) => {
let p = '';
const img = !!opts.inlineImages || !!opts.images; // naming flexibility
if (img)
p += '&inline_images=true';
this.sendRequest(`${this.getPath('news', opts)}${p}`, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
this.news = r.response;
for (const n of this.news)
n.image = new Doc(n.image); // Set image as document
resolve.call(this, this.news);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Index search
* @param {string} query Index search query
* @param {string} [object] Object
* @param {object} [opts] Options
* @param {boolean} [opts.metadata=false] Add meta data for each result
* @param {number} [opts.context] Context
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<array>} A promise to a list of index search records
* @function
*/
this.indexSearch = (query, object, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'Session.indexSearch';
opts = opts || {};
return new Promise((resolve, reject) => {
let p = `&request=${encodeURIComponent(query ? query : '')}`;
if (object)
p += `&object=${encodeURIComponent(object)}`;
if (opts.metadata === true)
p += '&_md=true';
if (opts.context)
p += `&context=${encodeURIComponent(opts.context)}`;
this.sendRequest(`${this.getPath('indexsearch', opts)}${p}`, undefined, (res, status) => {
const r = this.parseResponse(res, status);
this.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = this.getError(r.response, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
}
else {
resolve.call(this, r.response);
}
}, (err) => {
err = this.getError(err, undefined, origin);
if (!(opts.error || this.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get business object
* @param {string} name Business object name
* @param {string} [instance] Business object instance name, defaults to <code>js_<object name></code>
* @return {BusinessObject} Business object
* @function
*/
this.getBusinessObject = (name, instance) => {
const cacheKey = this.getBusinessObjectCacheKey(name, instance);
let obj = this.businessObjectCache[cacheKey];
if (!obj) {
obj = new BusinessObject(this, name, instance);
this.businessObjectCache[cacheKey] = obj;
}
return obj;
};
/**
* Get an external object
* @param {string} name External object name
* @function
*/
this.getExternalObject = (name) => {
return new ExternalObject(this, name);
};
/**
* Get a resource URL
* @param {string} code Resource code
* @param {string} [type=IMG] Resource type (IMG=image (default), ICO=Icon, CSS=stylesheet, JS=Javascript, HTML=HTML)
* @param {string} [object] Object name (not required for global resources)
* @param {string} [objId] Object ID (not required for global resources)
* @function
*/
this.getResourceURL = (code, type, object, objId) => {
return this.parameters.url + this.parameters.respath
+ '?code=' + encodeURIComponent(code) + '&type=' + encodeURIComponent(type || 'IMG')
+ (object ? '&object=' + encodeURIComponent(object) : '')
+ (objId ? '&objid=' + encodeURIComponent(objId) : '')
+ (this.authtoken ? '_x_simplicite_authorization_=' + encodeURIComponent(this.authtoken) : '');
};
params = params || {};
// Within the generic web UI if Simplicite is defined
const inUI = typeof globalThis.Simplicite !== 'undefined';
this.endpoint = params.endpoint || (inUI ? globalThis.Simplicite.ENDPOINT : "api" /* SessionParamEndpoint.API */);
this.authheader = params.authheader || this.constants.DEFAULT_AUTH_HEADER;
this.log = params.logHandler || ((...args) => {
// tslint:disable-next-line: no-console
console.log(args);
});
this.info = params.infoHandler || ((...args) => {
if (args && args.length === 1 && typeof args[0] === 'string')
// tslint:disable-next-line: no-console
console.info(`INFO - ${args[0]}`);
else
// tslint:disable-next-line: no-console
console.info('INFO', args);
});
this.warn = params.warningHandler || ((...args) => {
if (args && args.length === 1 && typeof args[0] === 'string')
// tslint:disable-next-line: no-console
console.warn(`WARN - ${args[0]}`);
else
// tslint:disable-next-line: no-console
console.warn(`WARN${args && args.length > 0 && args[0].message ? ` - ${args[0].message}` : ''}`, args);
});
this.error = params.errorHandler || ((...args) => {
if (args && args.length === 1 && typeof args[0] === 'string')
// tslint:disable-next-line: no-console
console.error(`ERROR - ${args[0]}`);
else
// tslint:disable-next-line: no-console
console.error(`ERROR${args && args.length > 0 && args[0].message ? ` - ${args[0].message}` : ''}`, args);
});
this.debugMode = !!params.debug;
this.debug = params.debugHandler || ((...args) => {
if (this.debugMode) {
if (args && args.length === 1 && typeof args[0] === 'string')
// tslint:disable-next-line: no-console
console.info(`DEBUG - ${args[0]}`);
else
// tslint:disable-next-line: no-console
console.log('DEBUG', args);
}
});
const purl = params.url || (inUI && globalThis.Simplicite.URL) || (globalThis.window && globalThis.window.location.origin);
this.debug('[simplicite] URL parameter = ' + purl);
if (purl) {
try {
params.scheme = purl.replace(/:.*$/, '');
const u = purl.replace(new RegExp('^' + params.scheme + '://'), '').split(':');
if (u.length === 1) {
params.host = u[0].replace(/\/.*$/, '');
params.port = params.scheme === 'http' ? 80 : 443;
params.root = u[0].replace(new RegExp('^' + params.host + '/?'), '');
}
else {
params.host = u[0];
params.port = parseInt(u[1].replace(/\/.*$/, ''), 10);
if (isNaN(params.port))
throw new Error('Incorrect port');
params.root = u[1].replace(new RegExp('^' + params.port + '/?'), '');
}
if (params.root === '/')
params.root = '';
}
catch (e) {
this.error('Unable to parse URL [' + purl + ']: ' + e.message);
return;
}
}
const scheme = params.scheme || (params.port === 443 ? 'https' : 'http');
if (scheme !== 'http' && scheme !== 'https') {
this.error('Incorrect scheme [' + params.scheme + ']');
return;
}
const host = params.host || 'localhost';
const port = params.port || 8080;
let root = params.root || '';
if (root === '/')
root = '';
let url = scheme + '://' + host;
if ((scheme === 'http' && port !== 80) || (scheme === 'https' && port !== 443))
url += ':' + port;
if (root !== '')
url += root.startsWith('/') ? root : '/' + root;
this.debug('[simplicite] Base URL = ' + url);
const ep = this.endpoint === 'uipublic' ? '' : '/' + this.endpoint;
this.parameters = {
scheme,
host,
port,
root,
url,
username: params.username,
password: params.password,
authtoken: params.authtoken,
timeout: (params.timeout || 30) * 1000, // milliseconds
compress: params.compress || true,
healthpath: (ep === '/ui' ? ep : '') + '/health?format=json',
loginpath: ep === '/api' ? '/api/login?format=json' : ep + '/json/app?action=session',
logoutpath: ep === '/api' ? '/api/logout?format=json' : ep + '/json/app?action=logout',
apppath: ep + '/json/app',
objpath: ep + '/json/obj',
extpath: ep + '/ext',
docpath: ep + '/raw/document',
respath: '/resource'
};
this.username = params.username || params.login; // naming flexibility
this.password = params.password || params.pwd; // naming flexibility
this.authtoken = params.authtoken || params.token; // explicit token with naming flexibility
if (!this.authtoken && inUI) {
// If in standard UI, get auth token from local storage or from the constant
const ls = globalThis.window ? globalThis.window.localStorage : null;
this.authtoken = ls ? ls.getItem('_authToken') : globalThis.Simplicite.AUTH_TOKEN;
}
this.ajaxkey = params.ajaxkey; // explicit Ajax key
if (!this.ajaxkey && inUI) {
// If in standard UI, get Ajax key from local storage or from the constant
const ls = globalThis.window ? globalThis.window.localStorage : null;
this.ajaxkey = ls ? ls.getItem('_ajaxKey') : globalThis.Simplicite.AJAX_KEY;
}
this.businessObjectCache = new Map();
}
/**
* Get path
* @param {string} action Action
* @param {object} [opts] Options
* @param {string} [opts.businessCase] Business case label
*/
getPath(action, opts) {
const bc = opts && opts.businessCase ? `&_bc=${encodeURIComponent(opts.businessCase)}` : '';
return `${this.parameters.apppath}?action=${encodeURIComponent(action)}${bc}`;
}
}
/**
* Document
* @class
*/
class Doc {
/**
* Constructor
* @param [value] {string|object} Document name or value
* @param [value.name] Document name
* @param [value.mime] Document MIME type
* @param [value.content] Document content
*/
constructor(value) {
/**
* Get the document ID
* @return {string} ID
* @function
*/
this.getId = () => {
return this.id;
};
/**
* Get the document MIME type
* @return {string} MIME type
* @function
*/
this.getMIMEType = () => {
return this.mime;
};
/**
* Alias to <code>getMIMEType</code>
* @return {string} MIME type
* @function
*/
this.getMimeType = this.getMIMEType;
/**
* Set the document MIME type
* @param {string} mime MIME type
* @return {Doc} This document for chaining
* @function
*/
this.setMIMEType = (mime) => {
this.mime = mime;
return this; // Chain
};
/**
* Alias to <code>setMIMEType</code>
* @param {string} mime MIME type
* @function
*/
this.setMimeType = this.setMIMEType;
/**
* Get the document name
* @return {string} Name
* @function
*/
this.getName = () => {
return this.name;
};
/**
* Alias to <code>getName</code>
* @return {string} Name
* @function
*/
this.getFileName = this.getName;
/**
* Alias to <code>getName</code>
* @return {string} Name
* @function
*/
this.getFilename = this.getName;
/**
* Set the document name
* @param {string} name Name
* @return {Doc} This document for chaining
* @function
*/
this.setName = (name) => {
this.name = name;
return this; // Chain
};
/**
* Alias to <code>setName</code>
* @param {string} name Name
* @function
*/
this.setFileName = this.setName;
/**
* Alias to <code>setName</code>
* @param {string} name Name
* @function
*/
this.setFilename = this.setName;
/**
* Get the document content (encoded in base 64)
* @return {string} Content
* @function
*/
this.getContent = () => {
return this.content;
};
/**
* Get the document thumbnail (encoded in base 64)
* @return {string} Thumbnail
* @function
*/
this.getThumbnail = () => {
return this.thumbnail;
};
/**
* Get the document content as an array buffer
* @return {ArrayBuffer} Content as an array buffer
* @function
*/
this.getContentAsArrayBuffer = () => {
return this.getBuffer(this.content).buffer;
};
/**
* Get the document thumbnail as an array buffer
* @return {ArrayBuffer} Thumbnail as an array buffer
* @function
*/
this.getThumbnailAsArrayBuffer = () => {
return this.getBuffer(this.thumbnail || '').buffer;
};
/**
* Get the document content as a text
* @return {string} Content as plain text
* @function
*/
this.getContentAsText = () => {
return this.getBuffer(this.content).toString('utf-8');
};
/**
* Set the document content
* @param {string} content Content (encoded in base 64)
* @return {Doc} This document for chaining
* @function
*/
this.setContent = (content) => {
this.content = this.cleanContent(content);
return this; // Chain
};
/**
* Set the document content from plain text string
* @param {string} content Content as plain text string
* @return {Doc} This document for chaining
* @function
*/
this.setContentFromText = (content) => {
this.content = Buffer.from(content, 'utf-8').toString('base64');
return this; // Chain
};
/**
* Get the document data URL
* @param {boolean} [thumbnail=false] Thumbnail? If thumbnail does not exists the content is used.
* @return {string} Data URL or nothing if content is empty
* @function
*/
this.getDataURL = (thumbnail) => {
if (this.content)
return 'data:' + this.mime + ';base64,' + (thumbnail && this.thumbnail ? this.thumbnail : this.content);
};
/**
* Load file
* @param file File to load
* @return {promise<Doc>} A promise to the document
* @function
*/
this.load = (file) => __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
if (file) {
this.name = file.name;
this.mime = file.type;
const reader = new FileReader();
reader.onload = () => {
this.content = reader.result ? this.cleanContent(reader.result) : '';
resolve(this);
};
reader.readAsDataURL(file); // this sets the result as a string
}
else {
this.content = '';
resolve(this);
}
}
catch (e) {
reject(e);
}
});
});
/**
* Get the document as a plain value object
* @return {object} Value object
* @function
*/
this.getValue = () => {
return {
id: this.id,
name: this['filename'] && !this.name ? this['filename'] : this.name, // Backward compatibility
mime: this.mime,
content: this.content,
thumbnail: this.thumbnail
};
};
Object.assign(this, typeof value == 'string' ? { name: value } : value || {});
// Backward compatibility
if (this['filename'] && !this.name) {
this.name = this['filename'];
this['filename'] = undefined;
}
}
cleanContent(content) {
return content.startsWith('data:') ? content.replace(/data:.*;base64,/, '') : content;
}
/**
* Get the document content as a buffer
* @param {any} data Content data
* @return {buffer} Content data as buffer
* @private
*/
getBuffer(data) {
return Buffer.from(data, 'base64');
}
}
/**
* Grant (user).
* <br/><span style="color: red;">You <strong>should never</strong> instanciate this class directly
* but rather use it from the <code>data</code> variable got using <code>getGrant</code></span>.
* @class
*/
class Grant {
/**
* Constructor
* @param grant {object} Grant object
*/
constructor(grant) {
/**
* Get user ID
* @return {string} User ID
* @function
*/
this.getUserId = () => {
return this.userid;
};
/**
* Get username
* @return {string} Username
* @function
*/
this.getUsername = () => {
return this.login;
};
/**
* Alias to <code>getUsername</code>
* @return {string} Login
* @function
*/
this.getLogin = this.getUsername; // Naming flexibility
/**
* Get user language
* @return {string} User language
* @function
*/
this.getLang = () => {
return this.lang;
};
/**
* Get email address
* @return {string} Email address
* @function
*/
this.getEmail = () => {
return this.email;
};
/**
* Get first name
* @return {string} First name
* @function
*/
this.getFirstname = () => {
return this.firstname;
};
/**
* Alias to <code>getFirstname</code>
* @return {string} First name
* @function
*/
this.getFirstName = this.getFirstname; // Naming flexibility
/**
* Get last name
* @return {string} Last name
* @function
*/
this.getLastname = () => {
return this.lastname;
};
/**
* Alias to <code>getLastname</code>
* @return {string} Last name
* @function
*/
this.getLastName = this.getLastname; // Naming flexibility
/**
* Get picture data URL
* @return {Doc} Picture data URL
* @function
*/
this.getPictureURL = () => {
if (this.picture)
return 'data:' + this.picture.mime + ';base64,' + this.picture.content;
};
/**
* Has responsibility
* @param {string} group Group name
* @return {boolean} True if user has a responsibility on the specified group
* @function
*/
this.hasResponsibility = (group) => {
return this.responsibilities && this.responsibilities.indexOf(group) !== -1;
};
/**
* Get system parameter value
* @param {string} name System parameter name
* @return {string} System parameter value
* @function
*/
this.getSystemParameter = (name) => {
return this.sysparams ? this.sysparams[name] || '' : '';
};
/**
* Alias to <code>getSystemParameter</code>
* @param {string} name System parameter name
* @return {string} System parameter value
* @funtion
*/
this.getSysParam = this.getSystemParameter;
/**
* Get text value
* @param {string} code Text code
* @return {string} Text value
*/
this.T = (code) => {
return this.texts ? this.texts[code] || '' : '';
};
Object.assign(this, grant);
}
}
/**
* Business object meta data.
* <br/><span style="color: red;">You <strong>should never</strong> instanciate this class directly
* but rather use it from the <code>metadata</code> variable of your <code>BusinessObject</code> instances</span>.
* @class
*/
class BusinessObjectMetadata {
/**
* Constructor
* @param {string} name Business object name
* @param {string} [instance] Business object instance name, defaults to <code>js_<object name></code>
*/
constructor(name, instance) {
this.name = name;
this.instance = instance;
this.rowidfield = constants.DEFAULT_ROW_ID_NAME;
this.label = name;
this.help = '';
this.fields = new Array();
}
}
/**
* Business object.
* <br/><span style="color: red;">ou <strong>should never</strong> instanciate this class directly
* but rather call <code>getBusinessObject</code> to get a cached instance</span>.
* @class
*/
class BusinessObject {
/**
* Constructor
* @param {Session} ses Session
* @param {string} name Business object name
* @param {string} [instance] Business object instance name, defaults to <code>js_<object name></code>
*/
constructor(ses, name, instance) {
/**
* Get meta data
* @param {object} [opts] Options
* @param {number} [opts.context] Context
* @param {string} [opts.contextParam] Context parameter
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<BusinessObjectMetadata>} A promise to the object's meta data (also available as the <code>metadata</code> member)
* @function
*/
this.getMetaData = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.getMetaData';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
let p = '';
if (opts.context)
p += '&context=' + encodeURIComponent(opts.context);
if (opts.contextParam)
p += '&contextparam=' + encodeURIComponent(opts.contextParam);
ses.sendRequest(this.path + '&action=metadata' + p, undefined, (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.metadata = r.response;
resolve.call(this, this.metadata);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get meta data (alias to getMetaData)
* @function
*/
this.getMetadata = this.getMetaData;
/**
* Get name
* @return {string} Name
* @function
*/
this.getName = () => {
return this.metadata.name;
};
/**
* Get instance name
* @return {string} Instance name
* @function
*/
this.getInstance = () => {
return this.metadata.instance;
};
/**
* Get display label
* @return {string} Display label
* @function
*/
this.getLabel = () => {
return this.metadata.label;
};
/**
* Get help
* @return {string} Help
* @function
*/
this.getHelp = () => {
return this.metadata.help;
};
/**
* Get all fields definitions
* @return {array} Array of field definitions
* @function
*/
this.getFields = () => {
return this.metadata.fields;
};
/**
* Get a field definition
* @param {string} fieldName Field name
* @return {object} Field definition
* @function
*/
this.getField = (fieldName) => {
const fs = this.getFields();
let n = 0;
while (n < fs.length && fs[n].name !== fieldName)
n++;
if (n < fs.length)
return fs[n];
};
/**
* Get row ID field name
* @return {string} Row ID field name
* @function
*/
this.getRowIdFieldName = () => {
return this.metadata.rowidfield;
};
/**
* Get row ID field definition
* @return {object} Row ID field definition
* @function
*/
this.getRowIdField = () => {
return this.getField(this.getRowIdFieldName());
};
/**
* Get links
* @return {array} Array of links
* @function
*/
this.getLinks = () => {
return this.metadata.links;
};
/**
* Get field type
* @param {(string|object)} field Field name or definition
* @return {string} Type (one of <code>constants.TYPE_*</code>)
* @function
*/
this.getFieldType = (field) => {
if (typeof field === 'string')
field = this.getField(field);
if (field)
return field.type;
};
/**
* Get field label
* @param {(string|object)} field Field name or definition
* @return {string} Field label
* @function
*/
this.getFieldLabel = (field) => {
if (typeof field === 'string')
field = this.getField(field);
if (field)
return field.label;
};
/**
* Get value of field for item (or current item)
* @param {(string|object)} field Field name or definition
* @param {object} [item] Item (defaults to current item)
* @return {string|Doc} Value
* @function
*/
this.getFieldValue = (field, item) => {
if (!item)
item = this.item;
if (field && item) {
const val = item[typeof field === 'string' ? field : field.name];
if (val && val.mime) // Document?
return new Doc(val);
else
return val;
}
};
/**
* Get the list value of a list of values field for item (or current item)
* @param {(string|object)} field Field name or definition
* @param {object} [item] Item (defaults to current item)
* @return {string} List value
* @function
*/
this.getFieldListValue = (field, item) => {
if (typeof field === 'string')
field = this.getField(field);
const val = this.getFieldValue(field, item);
return field && field.listOfValues ? this.getListValue(field.listOfValues, val) : val;
};
/**
* Get the list colors of a list of values field for item (or current item)
* @param {(string|object)} field Field name or definition
* @param {object} [item] Item (defaults to current item)
* @return {string} List color and bgcolor
* @function
*/
this.getFieldListColors = (field, item) => {
if (typeof field === 'string')
field = this.getField(field);
const val = this.getFieldValue(field, item);
return field && field.listOfValues ? this.getListColors(field.listOfValues, val) : val;
};
/**
* Get the data URL of an inlined document/image field for item (or current item)
* @param {(string|object)} field Field name or definition
* @param {object} [item] Item (defaults to current item)
* @return {string} Document/image field data URL (or nothing if the field is not of document/image type or if it is not inlined or if it is empty)
* @function
*/
this.getFieldDataURL = (field, item) => {
if (typeof field !== 'string')
field = field.fullinput || field.name;
const val = this.getFieldValue(field, item);
if (val && val.mime) // Inlined
return 'data:' + val.mime + ';base64,' + (val.content || val.thumbnail);
};
/**
* Get the field's value as document/image for item (or current item)
* @param {(string|object)} field Field name or definition
* @param {object} [item] Item (defaults to current item)
* @return {string|Doc} Document/image (or nothing if the field is not of document/image type or if it is empty)
* @function
*/
this.getFieldDocument = (field, item) => {
if (typeof field !== 'string')
field = field.fullinput || field.input || field.name;
const val = this.getFieldValue(field, item);
if (val && val.mime)
return new Doc(val);
else
return val;
};
/**
* Get the URL of a document/image field for item (or current item)
* @param {(string|object)} field Field name or definition
* @param {object} [item] Item (defaults to current item)
* @param {boolean} [thumbnail=false] Thumbnail?
* @return {string} Document/image field URL (or nothing if the field is not of document/image type or if it is empty)
* @function
*/
this.getFieldDocumentURL = (field, item, thumbnail) => {
if (typeof field !== 'string')
field = field.fullinput || field.input || field.name;
let val = this.getFieldValue(field, item);
if (val && val.mime) // Inlined
val = val.id;
if (val)
return this.session.parameters.url + this.session.parameters.docpath
+ '?object=' + encodeURIComponent(this.metadata.name)
+ '&inst=' + encodeURIComponent(this.metadata.instance)
+ '&field=' + encodeURIComponent(field)
+ '&row_id=' + encodeURIComponent(this.getRowId(item))
+ '&doc_id=' + encodeURIComponent(val)
+ (thumbnail ? '&thumbnail=true' : '')
+ (this.session.authtoken ? '&_x_simplicite_authorization_=' + encodeURIComponent(this.session.authtoken) : '');
};
/**
* Get list item value for code
* @param {array} list List of values
* @param {string} code Code
* @return {string} Value
* @function
*/
this.getListValue = (list, code) => {
if (list) {
for (const l of list) {
if (l.code === code)
return l.value;
}
}
return code;
};
/**
* Get list item colors (color and background color) for code
* @param {array} list List of values
* @param {string} code Code
* @return {any} Colors
* @function
*/
this.getListColors = (list, code) => {
if (list) {
for (const l of list) {
if (l.code === code)
return { color: l.color, bgcolor: l.bgcolor };
}
}
return { color: 'inherit', bgcolor: 'inherit' };
};
/**
* Set value of field for item (or current item)
* @param {(string|object)} field Field name or definition
* @param {(string|object)} value Value
* @param {object} [item] Item (defaults to current item)
* @function
*/
this.setFieldValue = (field, value, item) => {
if (!item)
item = this.item;
if (field && item) {
item[typeof field === 'string' ? field : field.name] = value instanceof Doc ? value.getValue() : value;
}
};
/**
* Reset values of item (or crrent item)
* @param {object} [item] Item (defaults to current item)
*/
this.resetValues = (item) => {
if (!item)
item = this.item;
for (const v in item)
delete item[v];
};
/**
* Set values of item (or current item)
* @param {object|FormData} data Data (plain object or form data)
* @param {object} [item] Item (defaults to current item)
*/
this.setFieldValues = (data, item) => __awaiter(this, void 0, void 0, function* () {
if (!item)
item = this.item;
// Convert form data to plain object
let dt;
if (data instanceof FormData) {
dt = {};
data.forEach((v, k) => dt[k] = v);
}
else {
dt = data;
}
return new Promise(resolve => {
const promises = [];
for (const k of Object.keys(dt)) {
const v = dt[k];
if (v instanceof File)
promises.push(new Promise(r => {
new Doc().load(v).then(doc => {
this.setFieldValue(k, doc);
r.call(this);
});
}));
else
this.setFieldValue(k, v);
}
Promise.allSettled(promises).then(() => resolve.call(this, item));
});
});
/**
* Is the field the row ID field?
* @param {object} field Field definition
* @return {boolean} True if the field is the row ID field
* @function
*/
this.isRowIdField = (field) => {
return !field.ref && field.name === this.metadata.rowidfield;
};
/**
* Is the field a timestamp field?
* @param {object} field Field definition
* @return {boolean} True if the field is a timestamp field
* @function
*/
this.isTimestampField = (field) => {
const n = field.name;
return !field.ref && (n === 'created_by' || n === 'created_dt' || n === 'updated_by' || n === 'updated_dt');
};
/**
* Get current filters
* @param {object} [opts] Options
* @param {number} [opts.context] Context
* @param {boolean} [opts.reset] Reset filters?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the object's filters (also available as the <code>filters</code> member)
* @function
*/
this.getFilters = (opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.getFilters';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
let p = '';
if (opts.context)
p += '&context=' + encodeURIComponent(opts.context);
if (opts.reset)
p += '&reset=' + !!opts.reset;
ses.sendRequest(this.path + '&action=filters' + p, undefined, (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.filters = r.response;
resolve.call(this, this.filters);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Build context option parameters
* @param {object} options Options
* @return {string} Option parameters
* @private
*/
this.getReqContextOption = (options) => {
return options.context ? `&context=${encodeURIComponent(options.context)}` : '';
};
/**
* Build options parameters
* @param {object} options Options
* @return {string} Option parameters
* @private
*/
this.getReqOptions = (options) => {
let opts = this.getReqContextOption(options);
const id = options.inlineDocs || options.inlineDocuments || options.inlineImages; // Naming flexibility
if (id)
opts += `&inline_documents=${encodeURIComponent(id.join ? id.join(',') : id)}`;
const it = options.inlineThumbs || options.inlineThumbnails; // Naming flexibility
if (it)
opts += `&inline_thumbnails=${encodeURIComponent(it.join ? it.join(',') : it)}`;
const io = options.inlineObjs || options.inlineObjects; // Naming flexibility
if (io)
opts += `&inline_objects=${encodeURIComponent(io.join ? io.join(',') : io)}`;
return opts;
};
/**
* Convert usual wildcards to filters wildcards
* @param {object} filter Filter
* @return {string} Filter with wildcards converted
* @private
*/
this.convertFilterWildCards = (filter) => {
return typeof filter === 'string' ? filter.replace(new RegExp('\\*', 'g'), '%').replace(new RegExp('\\?', 'g'), '_') : filter;
};
/**
* Build request parameters
* @param {object} data Data
* @param {boolean} [filters] Filters? Used to convert wildcards if needed
* @return {string} Request parameters
* @private
*/
this.getReqParams = (data, filters) => {
let p = '';
if (!data)
return p;
for (const i of Object.entries(data)) {
const k = i[0];
let d = i[1] || '';
if (d instanceof Doc)
d = d.getValue();
if (d.name && d.content) { // Document?
if (d.content.startsWith('data:')) // Flexibility = extract content from a data URL (just in case...)
d.content = d.content.replace(/data:.*;base64,/, '');
p += (p !== '' ? '&' : '') + k + '=' + encodeURIComponent('id|' + (d.id ? d.id : '0') + '|name|' + d.name + '|content|' + d.content);
}
else if (d.object && d.row_id) { // Object?
p += (p !== '' ? '&' : '') + k + '=' + encodeURIComponent('object|' + d.object + '|row_id|' + d.row_id);
}
else if (d.sort) { // Array?
for (const dd of d)
p += (p !== '' ? '&' : '') + k + '=' + encodeURIComponent(filters ? this.convertFilterWildCards(dd) : dd);
}
else {
p += (p !== '' ? '&' : '') + k + '=' + encodeURIComponent(filters ? this.convertFilterWildCards(d) : d);
}
}
return p;
};
/**
* Get count
* @param {object} [filters] Filters (defaults to current filters)
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {boolean} [opts.operations] Include operation fields results (sum, ...)
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the count
* @function
*/
this.getCount = (filters, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.getCount';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
let p = this.getReqContextOption(opts);
if (opts.operations === true)
p += '&_operations=true';
this.filters = filters || {};
ses.sendRequest(`${this.getPath('count', opts)}${p}`, this.getReqParams(this.filters, true), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug('[' + origin + '] HTTP status = ' + status + ', response type = ' + r.type);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.count = r.response.count;
this.page = r.response.page >= 0 ? r.response.page + 1 : undefined;
this.maxpage = r.response.maxpage >= 0 ? r.response.maxpage + 1 : undefined;
this.list = [];
resolve.call(this, this.count);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Search
* @param {object} [filters] Filters (defaults to current filters)
* @param {object} [opts] Options
* @param {number} [opts.page] Page number, a non paginated list is returned if not set
* @param {boolean} [opts.metadata=false] Refresh meta data?
* @param {boolean} [opts.visible] Return only visible fields?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<array>} Promise to a list of records (also available as the <code>list</code> member)
* @function
*/
this.search = (filters, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.search';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
let p = this.getReqOptions(opts);
if (opts.page > 0)
p += `&page=${opts.page - 1}`;
if (opts.metadata === true)
p += '&_md=true';
if (opts.visible === true)
p += '&_visible=true';
this.filters = filters || {};
ses.sendRequest(`${this.getPath('search', opts)}${p}`, this.getReqParams(this.filters, true), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
if (res.meta)
this.metadata = r.response.meta;
this.count = r.response.count;
this.page = r.response.page >= 0 ? r.response.page + 1 : undefined;
this.maxpage = r.response.maxpage >= 0 ? r.response.maxpage + 1 : undefined;
this.list = r.response.list;
resolve.call(this, this.list);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get
* @param {string} [rowId] Row ID (defaults to current item's row ID)
* @param {object} [opts] Options
* @param {boolean} [opts.metadata=false] Refresh meta data?
* @param {string[]} [opts.fields] List of field names to return, all fields are returned by default
* @param {string} [opts.treeview] Return the named tree view structure
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the record (also available as the <code>item</code> member)
* @function
*/
this.get = (rowId, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.get';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
let p = this.getReqOptions(opts);
const tv = opts.treeView;
if (tv)
p += `&treeview=${encodeURIComponent(tv)}`;
if (opts.fields) {
for (const f of opts.fields.length)
p += `&fields=${encodeURIComponent(f.replace('.', '__'))}`;
}
if (opts.metadata)
p += '&_md=true';
if (opts.social)
p += '&_social=true';
ses.sendRequest(`${this.getPath('get', opts)}&${this.metadata.rowidfield}=${encodeURIComponent(rowId || this.getRowId())}${p}`, undefined, (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug('[simplicite.BusinessObject.get] HTTP status = ' + status + ', response type = ' + r.type);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
if (r.response.meta)
this.metadata = r.response.meta;
if (r.response.data)
this.item = tv ? r.response.data.item : r.response.data;
else
this.item = tv ? r.response.item : r.response;
resolve.call(this, tv ? r.response : this.item);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get for create
* @param {object} [opts] Options
* @param {boolean} [opts.metadata=false] Refresh meta data?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the record to create (also available as the <code>item</code> member)
* @function
*/
this.getForCreate = (opts) => __awaiter(this, void 0, void 0, function* () {
opts = opts || {};
delete opts.treeview; // Inhibited in this context
delete opts.fields; // Inhibited in this context
opts.context = constants.CONTEXT_CREATE;
return this.get(constants.DEFAULT_ROW_ID, opts);
});
/**
* Get for update
* @param {string} [rowId] Row ID (defaults to current item's row ID)
* @param {object} [opts] Options
* @param {boolean} [opts.metadata=false] Refresh meta data?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the record to update (also available as the <code>item</code> member)
* @function
*/
this.getForUpdate = (rowId, opts) => __awaiter(this, void 0, void 0, function* () {
opts = opts || {};
delete opts.treeview; // Inhibited in this context
delete opts.fields; // Inhibited in this context
opts.context = constants.CONTEXT_UPDATE;
return this.get(rowId || this.getRowId(), opts);
});
/**
* Get for copy
* @param {string} [rowId] Row ID to copy (defaults to current item's row ID)
* @param {object} [opts] Options
* @param {boolean} [opts.metadata=false] Refresh meta data?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the record to create (also available as the <code>item</code> member)
* @function
*/
this.getForCopy = (rowId, opts) => __awaiter(this, void 0, void 0, function* () {
opts = opts || {};
delete opts.treeview; // Inhibited in this context
delete opts.fields; // Inhibited in this context
opts.context = constants.CONTEXT_COPY;
return this.get(rowId || this.getRowId(), opts);
});
/**
* Get for delete
* @param {string} [rowId] Row ID (defaults to current item's row ID)
* @param {object} [opts] Options
* @param {boolean} [opts.metadata=false] Refresh meta data?
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the record to delete (also available as the <code>item</code> member)
* @function
*/
this.getForDelete = (rowId, opts) => __awaiter(this, void 0, void 0, function* () {
opts = opts || {};
delete opts.treeview; // Inhibited in this context
delete opts.fields; // Inhibited in this context
opts.context = constants.CONTEXT_DELETE;
return this.get(rowId || this.getRowId(), opts);
});
/**
* Get specified or current item's row ID value
* @param {object} [item] Item (defaults to current item)
* @return {string} Item's row ID value
* @function
*/
this.getRowId = (item) => {
item = item || this.item;
if (item)
return item[this.getRowIdFieldName()];
};
/**
* Populate
* @param {object} [item] Item (defaults to current item)
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the populated record (also available as the <code>item</code> member)
* @function
*/
this.populate = (item, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.populate';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
if (item)
this.item = item;
ses.sendRequest(`${this.getPath('populate', opts)}${this.getReqOptions(opts)}`, this.getReqParams(this.item), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.item = r.response.data ? r.response.data : r.response;
resolve.call(this, this.item);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get the linked list for a list of values field and its specified value(s)
* @param {(string|object)} field Field name or definition
* @param {(string|object)} linkedField Linked field name or definition
* @param {string|boolean} [code] List of values code(s) (if multiple codes use ; as separator), defaults to current field value if empty, means "all" if true
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the populated record (also available as the <code>item</code> member)
* @function
*/
this.getFieldLinkedList = (field, linkedField, code, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.getFieldLinkedList';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
if (typeof field !== 'string')
field = field.fullinput || field.name;
if (typeof linkedField !== 'string')
linkedField = linkedField.fullinput || linkedField.name;
let all = false;
if (code === true) {
all = true;
code = undefined;
}
else if (typeof code === 'undefined') {
code = this.getFieldValue(field);
}
ses.sendRequest(this.getPath('getlinkedlist', opts), this.getReqParams({ origin: field, input: linkedField, code, all }), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug('[' + origin + '] HTTP status = ' + status + ', response type = ' + r.type);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.item = r.response.result ? r.response.result : r.response;
resolve.call(this, this.item);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Save (create or update depending on item row ID value)
* @param {object} [item] Item (defaults to current item)
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the saved record (also available as the <code>item</code> member)
* @function
*/
this.save = (item, opts) => __awaiter(this, void 0, void 0, function* () {
if (item)
this.item = item;
const rowId = this.item[this.metadata.rowidfield];
if (!rowId || rowId === constants.DEFAULT_ROW_ID)
return this.create(item, opts);
else
return this.update(item, opts);
});
/**
* Create (create or update)
* @param {object} [item] Item (defaults to current item)
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the created record (also available as the <code>item</code> member)
* @function
*/
this.create = (item, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.create';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
if (item)
this.item = item;
this.item.row_id = constants.DEFAULT_ROW_ID;
ses.sendRequest(`${this.getPath('create', opts)}${this.getReqOptions(opts)}`, this.getReqParams(this.item), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug('[' + origin + '] HTTP status = ' + status + ', response type = ' + r.type);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.item = r.response.data ? r.response.data : r.response;
resolve.call(this, this.item);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Update
* @param {object} [item] Item (defaults to current item)
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the updated record (also available as the <code>item</code> member)
* @function
*/
this.update = (item, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.update';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
if (item)
this.item = item;
ses.sendRequest(`${this.getPath('update', opts)}${this.getReqOptions(opts)}`, this.getReqParams(this.item), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.item = r.response.data ? r.response.data : r.response;
resolve.call(this, this.item);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Delete
* @param {object} [item] Item (defaults to current item)
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise (the <code>item</code> member is emptied)
* @function
*/
this.del = (item, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.del';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
if (item)
this.item = item;
ses.sendRequest(`${this.getPath('delete', opts)}&${this.metadata.rowidfield}=${encodeURIComponent(this.item[this.metadata.rowidfield])}`, undefined, (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
this.item = undefined;
delete r.response.undoredo;
resolve.call(this, r.response);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Invoke a custom action
* @param {string} action Action name
* @param {string} [rowId] Row ID
* @param {object} [opts] Options
* @param {function} [opts.parameters] Optional action parameters as key/value pairs
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<string|object>} A promise to the action result
* @function
*/
this.action = (action, rowId, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = `BusinessObject.action(${action})`;
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
const p = rowId ? `&${this.getRowIdFieldName()}=${encodeURIComponent(rowId)}` : '';
ses.sendRequest(`${this.getPath(action, opts)}${p}`, this.getReqParams(opts.parameters), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug('[' + origin + '] HTTP status = ' + status + ', response type = ' + r.type);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
const result = r.response.result;
resolve.call(this, result);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Build a pivot table
* @param {string} ctb Pivot table name
* @param {object} [opts] Options
* @param {boolean} [opts.cubes] Data as cubes?
* @param {object} [opts.filters] Filters, by default current filters are used
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} A promise to the pivot table data (also avialable as the <code>crosstabdata</code> member)
* @function
*/
this.crosstab = (ctb, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = `BusinessObject.crosstab(${ctb})`;
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
if (opts.filters)
this.filters = opts.filters;
ses.sendRequest(`${this.getPath(opts.cubes ? 'crosstabcubes' : 'crosstab', opts)}&crosstab=${encodeURIComponent(ctb)}`, this.getReqParams(opts.filters || this.filters, true), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
resolve.call(this, r.response);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Build a custom publication
* @param {string} prt Publication name
* @param {string} [rowId] Row ID
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<Doc>} A promise to the document of the publication
* @function
*/
this.print = (prt, rowId, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = `BusinessObject.print(${prt})`;
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
if (opts.filters)
this.filters = opts.filters;
let p = '';
if (opts.all)
p += '&all=' + !!opts.all;
if (opts.mailing)
p += '&mailing=' + !!opts.mailing;
if (rowId)
p += `&${this.getRowIdFieldName()}=${encodeURIComponent(rowId)}`;
ses.sendRequest(`${this.getPath('print', opts)}&printtemplate=${encodeURIComponent(prt)}${p}`, undefined, (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug('[' + origin + '] HTTP status = ' + status + ', response type = ' + r.type);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
resolve.call(this, new Doc(r.response));
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get place map data
* @param {string} pcm Place map name
* @param {string} [filters] Filters
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<any>} A promise to the place map data
* @function
*/
this.placemap = (pcm, filters, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = `BusinessObject.placemap(${pcm})`;
const ses = this.session;
this.filters = filters || {};
opts = opts || {};
return new Promise((resolve, reject) => {
if (opts.filters)
this.filters = opts.filters;
ses.sendRequest(`${this.getPath('placemap', opts)}&placemap=${encodeURIComponent(pcm)}`, this.getReqParams(this.filters, true), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug('[' + origin + '] HTTP status = ' + status + ', response type = ' + r.type);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
resolve.call(this, r.response);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Set an object parameter
* @param {string} param Parameter name
* @param {string} value Parameter value
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise
* @function
*/
this.setParameter = (param, value, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.setParameter';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
const p = { name: param };
if (value)
p.value = value;
ses.sendRequest(this.getPath('setparameter', opts), this.getReqParams(p), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
const result = r.response.result;
resolve.call(this, result);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get an object parameter
* @param {string} param Parameter name
* @param {object} [opts] Options
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the parameter value
* @function
*/
this.getParameter = (param, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'BusinessObject.getParameter';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
const p = { name: param };
ses.sendRequest(this.getPath('getparameter', opts), this.getReqParams(p), (res, status) => {
const r = ses.parseResponse(res, status);
ses.debug(`[${origin}] HTTP status = ${status}, response type = ${r.type}`);
if (r.type === 'error') {
const err = ses.getError(r.response, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
}
else {
const result = r.response.result;
resolve.call(this, result);
}
}, (err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Get an object resource URL
* @param {string} code Resource code
* @param {string} [type=IMG] Resource type (IMG=image (default), ICO=Icon, CSS=stylesheet, JS=Javascript, HTML=HTML)
* @return {string} Object resource URL
* @function
*/
this.getResourceURL = (code, type) => {
return this.session.getResourceURL(code, type, this.metadata.name, this.metadata.id);
};
this.session = ses;
const inst = instance || 'api_' + name;
this.metadata = new BusinessObjectMetadata(name, inst);
this.cacheKey = this.session.getBusinessObjectCacheKey(name, inst);
this.path = this.session.parameters.objpath + '?object=' + encodeURIComponent(name) + '&inst=' + encodeURIComponent(inst);
this.item = {};
this.filters = {};
this.list = [];
}
/**
* Get path
* @param {string} action Action
* @param {object} [opts] Options
* @param {string} [opts.businessCase] Business case label
*/
getPath(action, opts) {
const bc = opts && opts.businessCase ? `&_bc=${encodeURIComponent(opts.businessCase)}` : '';
return `${this.path}&action=${encodeURIComponent(action)}${bc}`;
}
}
/**
* External object meta data.
* <br/><span style="color: red;">You <strong>should never</strong> instanciate this class directly
* but rather use it from the <code>metadata</code> variable of your <code>ExternalObject</code> instances</span>.
* @class
*/
class ExternalObjectMetadata {
/**
* Constructor
* @param {string} name External object name
*/
constructor(name) {
this.name = name;
}
}
/**
* External object.
* <br/><span style="color: red;">ou <strong>should never</strong> instanciate this class directly
* but rather call <code>getExternalObject</code></span>.
* @class
*/
class ExternalObject {
/**
* Constructor
* @param {Session} ses Session
* @param {string} name Business object name
*/
constructor(ses, name) {
/**
* Get name
* @return {string} Name
* @function
*/
this.getName = () => {
return this.metadata.name;
};
/**
* Build URL-encoded parameters
* @param {object} params URL parameters as key/value pairs
* @return {string} URL-encoded parameters
* @function
*/
this.callParams = (params) => {
let p = '';
if (!params)
return p;
for (const i of Object.entries(params)) {
const k = i[0];
const v = i[1] || '';
if (v.sort) { // Array ?
for (const vv of v)
p += (p !== '' ? '&' : '') + k + '=' + encodeURIComponent(vv);
}
else {
p += (p !== '' ? '&' : '') + k + '=' + encodeURIComponent(v);
}
}
return p;
};
/**
* Call an external object
* @param {object} [params] Optional URL parameters
* @param {object|string|FormData} [data] Optional body data (for 'POST' and 'PUT' methods only)
* @param {object} [opts] Options
* @param {string} [opts.path] Absolute or relative path (e.g. absolute '/my/mapped/upath' or relative 'my/additional/path')
* @param {object} [opts.method] Optional method 'GET', 'POST', 'PUT' or 'DELETE' (defaults to 'GET' if data is not set or 'POST' if data is set)
* @param {function} [opts.contentType] Optional data content type (for 'POST' and 'PUT' methods only)
* @param {function} [opts.accept] Optional accepted response type (e.g. 'application/json")
* @param {function} [opts.error] Error handler function
* @param {string} [opts.businessCase] Business case label
* @return {promise<object>} Promise to the external object content
* @function
*/
this.call = (params, data, opts) => __awaiter(this, void 0, void 0, function* () {
const origin = 'ExternalObject.call';
const ses = this.session;
opts = opts || {};
return new Promise((resolve, reject) => {
let p = '';
if (params)
p = this.callParams(params);
if (opts.businessCase)
p += `_bc=${encodeURIComponent(opts.businessCase)}`;
const m = opts.method ? opts.method.toUpperCase() : (data ? 'POST' : 'GET');
const h = {};
if (opts.contentType) {
h['content-type'] = opts.contentType;
}
else if (data && !(data instanceof FormData)) { // Try to guess type...
h['content-type'] = typeof data === 'string' ? 'application/x-www-form-urlencoded' : 'application/json';
} // FormData = multipart/form-data with boundary string => handled by fetch
//if (ses.parameters.compress)
// h['content-encoding'] = 'gzip';
if (opts.accept)
h.accept = opts.accept === 'json' ? 'application/json' : opts.accept;
let b = ses.getBearerTokenHeader();
if (b) {
h[ses.authheader] = b;
}
else {
b = ses.getBasicAuthHeader();
if (b)
h[ses.authheader] = b;
}
const u = ses.parameters.url + (opts.path && opts.path.startsWith('/') ? opts.path : this.path + (opts.path ? '/' + opts.path : '')) + (p !== '' ? '?' + p : '');
const d = data ? (typeof data === 'string' || data instanceof FormData ? data : JSON.stringify(data)) : undefined;
ses.debug('[simplicite.ExternalObject.call] ' + m + ' ' + u + (d ? ' with ' + d : ''));
fetch(u, {
method: m,
headers: h,
//compress: ses.parameters.compress,
signal: AbortSignal.timeout(ses.parameters.timeout),
body: d
}).then((res) => {
const type = res.headers.get('content-type');
ses.debug(`[${origin}] HTTP status = ${res.status}, response content type = ${type}`);
if (type && type.startsWith('application/json')) { // JSON
res.json().then(jsonData => {
resolve.call(this, jsonData, res.status, res.headers);
}).catch((err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
}
else if (type && (type.startsWith('text/') || type.startsWith('application/yaml'))) { // Text
res.text().then(textData => {
resolve.call(this, textData, res.status, res.headers);
}).catch((err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
}
else { // Binary
res.arrayBuffer().then(binData => {
resolve.call(this, binData, res.status, res.headers);
}).catch((err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
}
}).catch((err) => {
err = ses.getError(err, undefined, origin);
if (!(opts.error || ses.error).call(this, err))
reject.call(this, err);
});
});
});
/**
* Alias to <code>call</code>
* @function
*/
this.invoke = this.call;
this.session = ses;
this.metadata = new ExternalObjectMetadata(name);
this.path = `${this.session.parameters.extpath}/${encodeURIComponent(name)}`;
}
}
export default {
constants,
session,
Session,
Doc,
Grant,
BusinessObject,
BusinessObjectMetadata,
ExternalObject
};
//# sourceMappingURL=simplicite.js.map