marklogic
Version:
The official MarkLogic Node.js client API.
298 lines (280 loc) • 8.72 kB
JavaScript
/*
* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
;
const qs = require('qs');
const requester = require('./requester.js');
const mlutil = require('./mlutil.js');
const Operation = require('./operation.js');
function execOutputTransform(headers, data) {
/*jshint validthis:true */
if ((headers == null) || (data == null)) {
return [];
}
let contentType = headers['content-type'][0];
if (typeof contentType === 'string' || contentType instanceof String) {
contentType = contentType.replace(/;.*$/, '');
}
switch(contentType) {
case 'application/x-unknown-content-type':
return {
format: 'binary',
datatype: 'node()',
value: data.content
};
case 'application/json':
return {
format: 'json',
datatype: 'node()',
value: data.content
};
case 'application/xml':
return {
format: 'xml',
datatype: 'node()',
value: data.content
};
}
const primitive = headers['x-primitive'][0];
let value = null;
switch(primitive) {
case 'node()':
return {
format: 'text',
datatype: 'node()',
value: data.content
};
case 'text()':
return {
format: 'text',
datatype: 'node()',
value: data.content
};
case 'attribute()':
return {
format: 'text',
datatype: 'attribute()',
value: data.content
};
case 'boolean':
const booleanValue = (typeof data.content === 'string')?(data.content.toLowerCase() === 'true'):data.content;
return {
format: 'text',
datatype: primitive,
value: Boolean(booleanValue)
};
case 'dateTime':
return {
format: 'text',
datatype: primitive,
value: new Date(data.content)
};
case 'byte':
case 'int':
case 'short':
case 'unsignedByte':
case 'unsignedInt':
case 'unsignedShort':
return {
format: 'text',
datatype: primitive,
value: Number(data.content)
};
case 'decimal':
case 'double':
case 'float':
value = data.content;
return {
format: 'text',
datatype: primitive,
value: isFinite(value) ? Number(value) : value
};
case 'integer':
case 'long':
case 'negativeInteger':
case 'nonNegativeInteger':
case 'nonPositiveInteger':
case 'positiveInteger':
case 'unsignedLong':
value = data.content;
if (isFinite(value)) {
const number = Number(value);
if (Math.abs(number) < 9007199254740992) {
value = number;
}
}
return {
format: 'text',
datatype: primitive,
value: value
};
case 'base64Binary':
case 'date':
case 'duration':
case 'gDay':
case 'gMonth':
case 'gMonthDay':
case 'gYear':
case 'gYearMonth':
case 'hexBinary':
case 'language':
case 'Name':
case 'NCName':
case 'QName':
case 'time':
case 'token':
return {
format: 'text',
datatype: primitive,
value: data.content
};
case 'anyAtomicType':
case 'anyURI':
case 'normalizedString':
case 'string':
case 'untypedAtomic':
return {
format: 'text',
datatype: 'string',
value: data.content
};
}
// TODO: x-path header?
return {
format: 'text',
datatype: 'other',
value: data.content
};
}
/**
* Evaluates JavaScript on the server; the user for the database client must have
* permission to evaluate code on the server and, in addition, permission
* to execute the actions performed by the source; takes a configuration object
* with the following named parameters or, as a shortcut, the source
* with or without variables.
* @method serverExec#eval
* @since 1.0
* @param {string} source - the JavaScript source code
* @param {object} [variables] - an object in which each property has
* a variable name as a key and a number, string, or boolean value
* @param {string|transactions.Transaction} [txid] - a string
* transaction id or Transaction object identifying an open
* multi-statement transaction
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the response
*/
function serverJavaScriptEval() {
/*jshint validthis:true */
return serverExec.call(this, 'JavaScript', mlutil.asArray.apply(null, arguments));
}
/**
* Evaluates XQuery on the server; the user for the database client must have
* permission to evaluate code on the server and, in addition, permission
* to execute the actions performed by the source; takes a configuration object
* with the following named parameters or, as a shortcut, the source
* with or without variables.
* @method serverExec#xqueryEval
* @since 1.0
* @param {string} source - the XQuery source code
* @param {object} [variables] - an object in which each property has
* a variable name as a key and a number, string, or boolean value; the key
* may be a namespaced name in Clark notation
* @param {string|transactions.Transaction} [txid] - a string
* transaction id or Transaction object identifying an open
* multi-statement transaction
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the response
*/
function serverXQueryEval() {
/*jshint validthis:true */
return serverExec.call(this, 'XQuery', mlutil.asArray.apply(null, arguments));
}
/**
* Invokes a JavaScript or XQuery module on the server; the user for the database client
* must have permission to invoke modules on the server and, in addition, permission
* to execute the actions performed by the module; takes a configuration object
* with the following named parameters or, as a shortcut, the path
* with or without variables.
* @method serverExec#invoke
* @since 1.0
* @param {string} path - the path of the module in the modules database
* for the REST server; the module must have been installed previously
* (typically with the {@link config.extlibs#write} function) using
* a filename extension (mjs, sjs, or xqy by default) registered for server
* JavaScript or XQuery in the server mime types table
* @param {object} [variables] - an object in which each property has
* a variable name as a key and a number, string, or boolean value; the key
* may be in Clark notation for namespaced XQuery variables
* @param {string|transactions.Transaction} [txid] - a string
* transaction id or Transaction object identifying an open
* multi-statement transaction
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the response
*/
function serverInvoke() {
/*jshint validthis:true */
return serverExec.call(this, 'invoke', mlutil.asArray.apply(null, arguments));
}
/** @ignore */
function serverExec(execName, args) {
/*jshint validthis:true */
const client = this;
let operationDesc = null;
let execType = null;
if (execName === 'invoke') {
operationDesc = 'invoke code on server';
execType = execName;
} else {
operationDesc = 'eval '+execName+' on server';
execType = execName.toLowerCase();
}
if (args.length === 0) {
throw new Error('must specify the source to '+operationDesc);
}
const isInvoke = (execType === 'invoke');
const arg = args[0];
let source = isInvoke ? arg.path : arg.source;
let variables = null;
let txid = null;
if (source !== void 0) {
variables = arg.variables;
txid = mlutil.convertTransaction(arg.txid);
} else {
source = arg;
if (args.length > 1) {
variables = args[1];
}
}
const body = {};
if (isInvoke) {
body.module = source;
} else {
body[execType] = source;
}
if (variables != null) {
body.vars = JSON.stringify(variables);
}
let endpoint = isInvoke? '/v1/invoke' : '/v1/eval';
let sep = '?';
if (txid != null) {
endpoint += sep+'txid='+mlutil.getTxidParam(txid);
sep = '&';
}
const requestOptions = mlutil.newRequestOptions(client.getConnectionParams(), endpoint, 'POST');
requestOptions.headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'multipart/mixed; boundary='+mlutil.multipartBoundary
};
mlutil.addTxidHeaders(requestOptions, txid);
const operation = new Operation(
operationDesc, client, requestOptions, 'single', 'multipart'
);
operation.requestBody = qs.stringify(body);
operation.outputTransform = execOutputTransform;
return requester.startRequest(operation);
}
module.exports = {
serverJavaScriptEval: serverJavaScriptEval,
serverXQueryEval: serverXQueryEval,
serverInvoke: serverInvoke
};