marklogic
Version:
The official MarkLogic Node.js client API.
909 lines (845 loc) • 34.1 kB
JavaScript
/*
* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
'use strict';
const http = require('http');
const https = require('https');
const mlutil = require('./mlutil.js');
const mllog = require('./mllog.js');
const documents = require('./documents.js');
const Graphs = require('./graphs.js');
const Rows = require('./rows.js');
const Values = require('./values.js');
const ExtLibs = require('./extlibs.js');
const RESTServerProperties = require('./rest-server-properties.js');
const Transactions = require('./transactions.js');
const Transforms = require('./transforms.js');
const ResourcesConfig = require('./resources-config.js');
const ResourcesExec = require('./resources-exec.js');
const serverExec = require('./server-exec.js');
const queryBuilder = require('./query-builder.js');
const patchBuilder = require('./patch-builder.js');
const valuesBuilder = require('./values-builder.js');
const planBuilder = require('./plan-builder.js');
const ctsQueryBuilder = require('./ctsquery-builder');
const Operation = require('./operation.js');
const requester = require('./requester.js');
const internal = require('./internal.js');
const proxy = require('./endpoint-proxy.js');
const dns = require('dns');
/**
* Provides functions to connect to a MarkLogic database and to build
* requests for the database.
* @namespace marklogic
*/
/**
* Provides functions that maintain patch replacement libraries
* on the REST server. The client must have been created for a user with the
* rest-admin role.
* @namespace config.patch.replace
*/
/**
* Reads a patch replacement library installed on the server.
* @method config.patch.replace#read
* @since 1.0
* @param {string} moduleName - the filename without a path for the patch replacement library
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the source code for the library
*/
/**
* Installs a patch replacement library on the server.
* @method config.patch.replace#write
* @since 1.0
* @param {string} moduleName - the filename without a path for the patch replacement library;
* the filename must end in an extension registered in the server's mime type mapping table
* for the mime type of the source code format; at present, the extension should be ".xqy"
* @param {object[]} [permissions] - the permissions controlling which users can read, update, or
* execute the patch replacement library
* @param {string} source - the source code for the patch replacement library; at present,
* the source code must be XQuery
*/
/**
* Deletes a patch replacement library from the server.
* @method config.patch.replace#remove
* @since 1.0
* @param {string} moduleName - the filename without a path for the patch replacement library
*/
/**
* Lists the patch replacement libraries installed on the server.
* @method config.patch.replace#list
* @since 1.0
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the list of replacement libraries
*/
/**
* Provides functions that maintain custom query binding
* or facet extensions on the REST server. The client must have been created
* for a user with the rest-admin role.
* @namespace config.query.custom
*/
/**
* Reads a custom query library installed on the server.
* @method config.query.custom#read
* @since 1.0
* @param {string} moduleName - the filename without a path for the custom query library
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the source code for the library
*/
/**
* Installs a custom query library on the server.
* @method config.query.custom#write
* @since 1.0
* @param {string} moduleName - the filename without a path for the custom query library;
* the filename must end in an extension registered in the server's mime type mapping table
* for the mime type of the source code format; at present, the extension should be ".xqy"
* @param {object[]} [permissions] - the permissions controlling which users can read, update, or
* execute the custom query library
* @param {string} source - the source code for the custom query library; at present,
* the source code must be XQuery
*/
/**
* Deletes a custom query library from the server.
* @method config.query.custom#remove
* @since 1.0
* @param {string} moduleName - the filename without a path for the custom query library
*/
/**
* Lists the custom query libraries installed on the server.
* @method config.query.custom#list
* @since 1.0
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the list of replacement libraries
*/
/**
* Provides functions that maintain query snippet extensions
* on the REST server. The client must have been created for a user with the
* rest-admin role.
* @namespace config.query.snippet
*/
/**
* Reads a query snippet library installed on the server.
* @method config.query.snippet#read
* @since 1.0
* @param {string} moduleName - the filename without a path for the query snippet library
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the source code for the library
*/
/**
* Installs a query snippet library on the server.
* @method config.query.snippet#write
* @since 1.0
* @param {string} moduleName - the filename without a path for the query snippet library;
* the filename must end in an extension registered in the server's mime type mapping table
* for the mime type of the source code format; at present, the extension should be ".xqy"
* @param {object[]} [permissions] - the permissions controlling which users can read, update, or
* execute the query snippet library
* @param {string} source - the source code for the query snippet library; at present,
* the source code must be XQuery
*/
/**
* Deletes a query snippet library from the server.
* @method config.query.snippet#remove
* @since 1.0
* @param {string} moduleName - the filename without a path for the query snippet library
*/
/**
* Lists the custom query libraries installed on the server.
* @method config.query.snippet#list
* @since 1.0
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives the list of replacement libraries
*/
function ExtlibsWrapper(extlibs, name, dir) {
if (!(this instanceof ExtlibsWrapper)) {
return new ExtlibsWrapper(extlibs, name, dir);
}
this.extlibs = extlibs;
this.name = name;
this.dir = dir;
}
ExtlibsWrapper.prototype.list = function listExtlibsWrapper() {
return this.extlibs.list.call(this.extlibs, this.dir);
};
ExtlibsWrapper.prototype.read = function readExtlibsWrapper() {
return this.extlibs.read.apply(
this.extlibs,
expandExtlibsWrapper.call(this, 'reading', mlutil.asArray.apply(null, arguments))
);
};
ExtlibsWrapper.prototype.remove = function removeExtlibsWrapper() {
return this.extlibs.remove.apply(
this.extlibs,
expandExtlibsWrapper.call(this, 'removing', mlutil.asArray.apply(null, arguments))
);
};
ExtlibsWrapper.prototype.write = function writeExtlibsWrapper() {
const args = expandExtlibsWrapper.call(this, 'writing', mlutil.asArray.apply(null, arguments));
const module = args[0];
const ext = mlutil.extension(module);
if (ext === null) {
throw new Error(module+' module for '+this.name+' library must have an extension of .mjs, .sjs, or .xqy');
}
switch(ext) {
case 'sjs':
args[0] = 'application/javascript';
break;
case 'mjs':
args[0] = 'application/vnd.marklogic-js-module';
break;
case 'xqy':
args[0] = 'application/xquery';
break;
default:
throw new Error(module+' module for '+this.name+' library does not have an .mjs, .sjs, or .xqy extension');
}
args.unshift(module);
return this.extlibs.write.apply(this.extlibs, args);
};
function expandExtlibsWrapper(action, args) {
/*jshint validthis:true */
const module = (args.length > 0) ? args[0] : null;
if (typeof module !== 'string' && !(module instanceof String)) {
throw new Error('no module name for '+action+' '+this.name+' library');
}
args[0] = this.dir + module;
return args;
}
/**
* A client object configured to write, read, query, and perform other
* operations on a database as a user. The client object is
* created by the {@link marklogic.createDatabaseClient} function.
* @namespace DatabaseClient
* @borrows serverExec#eval as DatabaseClient#eval
* @borrows serverExec#xqueryEval as DatabaseClient#xqueryEval
* @borrows serverExec#invoke as DatabaseClient#invoke
*/
/**
* Creates a database client to make database requests such as writes, reads,
* and queries. The constructor takes a configuration object with the following
* named parameters.
* @function marklogic.createDatabaseClient
* @since 1.0
* @param {string} [host=localhost] - the host with the REST server for the database
* @param {number} [port=8000] - the port with the REST server for the database
* @param {string} [database] - the name of the database to access, defaulting
* to the database for the AppServer on the port
* @param {string} user - the user with permission to access the database
* @param {string} password - the password for the user with permission to access
* the database
* @param {enum} [authType=digest] - the authentication type of digest|basic|certificate|kerberos|saml|cloud
* @param {boolean} [ssl=false] - whether the REST server uses SSL; when true,
* the connection parameters can include the
* {@link http://nodejs.org/api/https.html#https_https_request_options_callback|supplemental
* HTTPS options} specifiable for the node.js tls.connect() function.
* @param {object} [agent] - defaults to a connection pooling agent
* @param {string|string[]|Buffer|Buffer[]} [ca] - the trusted certificate(s), if
* required for SSL
* @param {string|Buffer} [key] - the private key to use for SSL
* @param {string|Buffer} [cert] - the public x509 certificate to use for SSL
* @param {Buffer} [pfx] - the public x509 certificate and private key as a single PKCS12 file
* to use for SSL
* @param {string} [passphrase] - the passphrase for the PKCS12 file
* @param {string} [token] - the SAML token to use for authentication with the REST server
* @returns {DatabaseClient} a client for accessing the database
* as the user
*/
function MarkLogicClient(connectionParams) {
if (!(this instanceof MarkLogicClient)) {
return new MarkLogicClient(connectionParams);
}
if (connectionParams == null) {
throw new Error('no connection parameters');
}
initClient(this, connectionParams);
/**
* Provides functions that write, read, query, or perform other operations
* on documents in the database. As a convenience, the same functions are
* provided on the database client.
* @name documents
* @since 1.0
* @memberof! DatabaseClient#
* @type {documents}
*/
this.documents = documents.create(this);
/**
* Provides functions for performing relational operations
* on indexed values and documents in the database.
* @name rows
* @since 2.1.1
* @memberof! DatabaseClient#
* @type {rows}
*/
this.rows = new Rows(this);
/**
* Provides functions that open, commit, or rollback multi-statement
* transactions.
* @name transactions
* @since 1.0
* @memberof! DatabaseClient#
* @type {transactions}
*/
this.transactions = new Transactions(this);
/**
* Provides functions that read, write, merge, remove, list, or query
* with SPARQL on triple graphs.
* @name graphs
* @since 1.0
* @memberof! DatabaseClient#
* @type {graphs}
*/
this.graphs = new Graphs(this);
/**
* Provides functions that submit get, put, post, or remove requests
* to resource service extensions.
* @name resources
* @since 1.0
* @memberof! DatabaseClient#
* @type {resources}
*/
this.resources = new ResourcesExec(this);
/**
* Provides functions that submit values queries to project
* tuples (rows) of values from documents.
* @name values
* @since 1.0
* @memberof! DatabaseClient#
* @type {values}
*/
this.values = new Values(this);
const configExtlibs = new ExtLibs(this);
/**
* Provides access to namespaces that configure the REST server for the client.
* The client must have been created for a user with the rest-admin role.
* @name config
* @since 1.0
* @memberof! DatabaseClient#
* @property {config.extlibs} extlibs - provides functions that
* maintain the extension libraries on the REST server
* @property {config.patch.replace} patch.replace - provides functions that
* maintain patch replacement libraries on the REST server
* @property {config.query.custom} query.custom - provides functions that
* maintain custom query binding or facet extensions on the REST server
* @property {config.query.snippet} query.snippet - provides functions that
* maintain query snippet extensions on the REST server
* @property {config.resources} resources - provides functions that
* maintain resource service extensions on the REST server
* @property {config.serverprops} serverprops - provides functions that
* modify the properties of the REST server
* @property {config.transforms} transforms - provides functions that
* maintain transform extensions on the REST server
*/
this.config = {
extlibs: configExtlibs,
patch: {
replace: new ExtlibsWrapper(configExtlibs, 'patch replace', '/marklogic/patch/apply/')
},
query: {
custom: new ExtlibsWrapper(configExtlibs, 'custom query', '/marklogic/query/custom/'),
snippet: new ExtlibsWrapper(configExtlibs, 'query snippet', '/marklogic/snippet/custom/')
},
resources: new ResourcesConfig(this),
serverprops: new RESTServerProperties(this),
transforms: new Transforms(this)
};
this.logger = null;
}
/** @ignore */
MarkLogicClient.prototype.getConnectionParams = function getConnectionParams() {
if(this.connectionParams === null) {
throw new Error('Connection has been closed.');
}
return this.connectionParams;
};
/**
* Tests if a connection is successful.
* @method DatabaseClient#checkConnection
* @since 2.1
* @returns an object with a connected property of true or false. In the false case
* it contains httpStatusCode and httpStatusMessage properties identifying the failure.
*/
MarkLogicClient.prototype.checkConnection = function checkConnection() {
const requestOptions = mlutil.newRequestOptions(this.connectionParams, '/v1/ping', 'HEAD');
const operation = new Operation(
'test operation', this, requestOptions, 'empty', 'empty'
);
operation.statusCodeValidator = function testStatusCodes(statusCode, response){
if (statusCode >= 300) {
this.responseStatusMessage = response.statusMessage;
}
return null;
};
operation.outputTransform = function addOutputTransform() {
let content = null;
if(this.responseStatusCode <300) {
content = {connected: true};
} else {
content = {connected: false,
httpStatusCode: this.responseStatusCode,
httpStatusMessage: this.responseStatusMessage
};
}
return content;
};
return requester.startRequest(operation);
};
// placate lint
MarkLogicClient.prototype['Eval'.toLowerCase()] = serverExec.serverJavaScriptEval;
MarkLogicClient.prototype.xqueryEval = serverExec.serverXQueryEval;
MarkLogicClient.prototype.invoke = serverExec.serverInvoke;
/**
* Creates one or more JSON documents for a collection; takes a collection name string and
* the content for one or more JSON documents (or an array of content). The
* server assigns uri identifiers to the documents created with the content.
* The {@link documents#write} function is less simple but more complete,
* accepting a uri identifier and metadata (such as multiple collections)
* and optionally transforming each document on the server.
* @method DatabaseClient#createCollection
* @since 1.0
* @param {string} collection - the name of the collection for the documents
* @param {object|object[]} content - the objects with the content for the
* documents
* @returns {ResultProvider} an object whose result() function takes
* a success callback receiving an array of uri strings for the created documents.
*/
MarkLogicClient.prototype.createCollection = function createCollection() {
const argLen = arguments.length;
if (argLen < 2) {
throw new Error('must specify both a collection and content objects for document create');
}
const collection = arguments[0];
if (typeof collection !== 'string' && !(collection instanceof String)) {
throw new Error('must specify at least one collection for document create');
}
let contentArray = arguments[1];
let i = 0;
if (!Array.isArray(contentArray)) {
i = 1;
contentArray = arguments;
}
const documentList = [];
for (; i < contentArray.length; i++) {
documentList.push({
collections: collection,
contentType: 'application/json',
directory: '/',
extension: '.json',
content: contentArray[i]
});
}
return documents.writeImpl.call(this.documents, true, documentList);
};
/**
* Probes whether a document exists; takes a uri string identifying the document.
* The {@link documents#probe} function is less simple but more complete,
* returning an object with additional information.
* @method DatabaseClient#probe
* @since 1.0
* @param {string} uri - the uri for the database document
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives a boolean.
*/
MarkLogicClient.prototype.probe = function probeClient() {
return documents.probeImpl.call(this.documents, true, mlutil.asArray.apply(null, arguments));
};
/**
* Executes a query to retrieve the content of documents in a collection,
* optionally adding a query built by a {@link queryBuilder} to retrieve
* a subset of the collection documents.
* The {@link documents#query} function is less simple but more complete,
* potentially transforming documents on the server and returning
* an envelope object for each document with the uri and metadata
* as well as the content.
* an object for each document with additional information.
* @method DatabaseClient#queryCollection
* @since 1.0
* @param {string} collection - the name of the document collection
* @param {object} [query] - a query built by a {@link queryBuilder}
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives an array with the content of the
* documents.
*/
MarkLogicClient.prototype.queryCollection = function queryClient(collection, builtQuery) {
if (typeof collection !== 'string' && !(collection instanceof String)) {
throw new Error('must specify at least one collection for quick document queries');
}
if (builtQuery == null) {
builtQuery = queryBuilder.builder.where();
}
return documents.queryImpl.call(this.documents, collection, true, builtQuery);
};
/**
* Reads one or more documents; takes one or more uri strings or
* an array of uri strings. The {@link documents#read} function
* is less simple but more complete, potentially transforming
* documents on the server and returning an envelope object for
* each document with the uri and metadata as well as the content.
* @method DatabaseClient#read
* @since 1.0
* @param {string|string[]} uris - the uri string or an array of uri strings
* for the database documents
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives an array with the content of the documents.
*/
MarkLogicClient.prototype.read = function readClient() {
return documents.readImpl.call(this.documents, true, mlutil.asArray.apply(null, arguments));
};
/**
* Removes one or more database documents; takes one or more uri strings
* identifying the document.
* The {@link documents#remove} function is less simple but more complete.
* @method DatabaseClient#remove
* @since 1.0
* @param {string|string[]} uris - the uri string or an array of uri strings
* identifying the database documents
* @returns {ResultProvider} an object whose result() function takes
* a success callback that receives an array of the uris identifying
* the removed documents.
*/
MarkLogicClient.prototype.remove = function removeClient() {
return documents.removeImpl.call(
this.documents,
true,
mlutil.asArray.apply(null, arguments)
);
};
/**
* Removes all documents in a collection; takes the uri string identifying
* the collection.
* The {@link documents#removeAll} function is less simple but more complete,
* supporting deleting a directory or all documents in the database.
* @method DatabaseClient#removeCollection
* @since 1.0
* @param {string} collection - the collection whose documents should be
* deleted
* @returns {ResultProvider} an object with a result() function takes
* a success callback that receives the uri string identifying the removed
* collection.
*/
MarkLogicClient.prototype.removeCollection = function removeCollectionClient(collection) {
if (typeof collection !== 'string' && !(collection instanceof String)) {
throw new Error('must specify at least one collection for quick document remove');
}
return documents.removeCollectionImpl.call(this.documents, true, {collection: collection});
};
/**
* Inserts or updates one or more documents for a collection; takes a collection name
* string and an object that maps document uris to document content.
* The {@link documents#write} function is less simple but more complete,
* accepting a uri identifier and metadata (such as multiple collections)
* and optionally transforming each document on the server.
* @method DatabaseClient#writeCollection
* @since 1.0
* @param {string} collection - the name of the collection for the documents
* @param {object} documents - an object in which every key is the uri string
* for a document and every value is the content for the document
* @returns {ResultProvider} an object whose result() function takes
* a success callback receiving an array of uri strings for the written documents.
*/
MarkLogicClient.prototype.writeCollection = function writeClient() {
const argLen = arguments.length;
if (argLen < 2) {
throw new Error('must specify both a collection and mapping object for quick document write');
}
const collection = arguments[0];
if (typeof collection !== 'string' && !(collection instanceof String)) {
throw new Error('must specify at least one collection for quick document write');
}
const map = arguments[1];
const uris = Object.keys(map);
if (uris.length === 0) {
throw new Error('must map at least one document uri to content for quick document write');
}
const documentList = [];
let uri = null;
let i = 0;
for (; i < uris.length; i++) {
uri = uris[i];
documentList.push({
uri: uri,
collections: collection,
content: map[uri]
});
}
return documents.writeImpl.call(this.documents, true, documentList);
};
MarkLogicClient.prototype.release = function releaseMarkLogicClient() {
releaseClient(this);
};
/**
* Supplies a logger to use for database interactions or, instead, takes
* a logging level from the debug|info|warn|error|silent enumeration (where silent
* is the initial setting) for the default console logger.
* @method DatabaseClient#setLogger
* @since 1.0
* @param {object} logger - an object providing debug(), info(), warn(), and error()
* logging methods such as a logger provided by the
* {@link https://github.com/trentm/node-bunyan|Bunyan} or
* {@link https://github.com/flatiron/winston|Winston} logging libraries.
* @param {boolean} [isErrorFirst] - whether an error should be logged as the first parameter;
* must be provided as true for Bunyan (but not for Winston); defaults to false
*/
MarkLogicClient.prototype.setLogger = function setClientLogger() {
const argLen = arguments.length;
if (argLen < 1) {
throw new Error('must provide a logger as the first argument');
}
const arg = arguments[0];
if (typeof arg === 'string' || arg instanceof String) {
const logger = (argLen === 1) ? this.getLogger() : null;
if (logger instanceof mllog.ConsoleLogger) {
logger.setLevel(arg);
} else {
throw new Error('first argument is not a logger');
}
} else {
this.logger = new mllog.DelegatingLogger(arg);
if (argLen > 1 && (typeof arguments[1] === 'boolean')) {
this.logger.isErrorFirst = arguments[1];
}
}
};
/** @ignore */
MarkLogicClient.prototype.getLogger = function getClientLogger() {
let logger = this.logger;
if (logger == null) {
logger = new mllog.ConsoleLogger();
this.logger = logger;
}
return logger;
};
/**
* An object representing a timestamp on the server.
* @typedef {object} DatabaseClient.Timestamp
* @since 2.1.1
*/
/**
* Creates a timestamp object.
* @method DatabaseClient#createTimestamp
* @since 2.1.1
* @param {string} [value] - a timestamp value as a string.
* @returns {Timestamp} - a Timestamp object.
*/
MarkLogicClient.prototype.createTimestamp = function databaseCreateTimestamp(value) {
const ts = new mlutil.Timestamp(value ? value : null);
return ts;
};
/**
* Supplies a new authentication token to be used in subsequent requests
* instead of the current authentication token.
*
* Note: the token must be a SAML authentication token.
* @method DatabaseClient#setAuthToken
* @param {string} token - an authentication token
* @since 2.2.0
*/
MarkLogicClient.prototype.setAuthToken = function setAuthToken() {
const argLen = arguments.length;
if (argLen < 1) {
throw new Error('must provide a token as the first argument');
}
const arg = arguments[0];
const connectionParams = this.connectionParams;
const authType = connectionParams.authType.toUpperCase();
if (authType !== 'SAML') {
throw new Error('assigning new token only supported for SAML client');
}
if (typeof arg !== 'string' && !(arg instanceof String)) {
throw new Error('token assigned to SAML client must be string');
}
connectionParams.auth = 'SAML token='+arg;
};
/** @ignore */
MarkLogicClient.prototype.createProxy = function createProxy(serviceDeclaration) {
return proxy.init(this, serviceDeclaration);
};
MarkLogicClient.prototype.serviceCaller = function serviceCaller(serviceDeclaration, endpointDeclarations) {
return proxy.initService(this, serviceDeclaration, endpointDeclarations);
};
MarkLogicClient.prototype.endpointCaller =function endpointCaller(endpointDeclaration) {
return proxy.initEndpoint(this, endpointDeclaration);
};
function initClient(client, inputParams) {
const connectionParams = {};
let isSSL = (inputParams.ssl == null) ? false : inputParams.ssl;
if(inputParams.authType && inputParams.authType.toString().toLowerCase() === 'cloud' && !isSSL){
isSSL = true;
}
const keys = ['host', 'port', 'database', 'user', 'password', 'authType', 'token', 'basePath','apiKey','accessTokenDuration',
'enableGzippedResponses', 'oauthToken'];
if(isSSL) {
keys.push('ca', 'cert', 'ciphers', 'clientCertEngine', 'crl', 'dhparam', 'ecdhCurve', 'honorCipherOrder', 'key', 'passphrase',
'pfx', 'rejectUnauthorized', 'secureOptions', 'secureProtocol', 'servername', 'sessionIdContext', 'highWaterMark');
}
for (let i=0; i < keys.length; i++) {
const key = keys[i];
const value = inputParams[key];
if (value != null) {
connectionParams[key] = value;
} else if (key === 'host') {
connectionParams.host = 'localhost';
} else if (key === 'port') {
connectionParams.port = 8000;
} else if (key === 'authType') {
connectionParams.authType = 'DIGEST';
}
}
connectionParams.lookup = prefLookup;
const authType = connectionParams.authType.toUpperCase();
if ((authType === 'DIGEST' || authType === 'BASIC') &&
((connectionParams.user == null) || (connectionParams.password == null))) {
throw new Error('cannot create client without user or password for '+
connectionParams.host+' host and '+
connectionParams.port+' port' + ' with authType '+authType
);
}
client.connectionParams = connectionParams;
if (authType === 'SAML') {
const token = connectionParams.token;
if (typeof token !== 'string' && !(token instanceof String)) {
throw new Error('cannot create SAML client without valid token string for '+
connectionParams.host+' host and '+
connectionParams.port+' port'
);
}
connectionParams.auth = 'SAML token='+token;
} else if(authType === 'CLOUD') {
if(!connectionParams.apiKey) {
throw new Error('apiKey needed for MarkLogic cloud authentication.');
}
connectionParams.port = 443;
} else if(authType === 'OAUTH' && !connectionParams.oauthToken){
throw new Error('oauthToken required for OAuth authentication. ');
}
else if (authType !== 'DIGEST' &&
authType !== 'CERTIFICATE') {
connectionParams.auth =
connectionParams.user+':'+connectionParams.password;
}
const noAgent = (inputParams.agent == null);
const agentOptions = noAgent ? {
keepAlive: true
} : null;
if (isSSL) {
client.request = https.request;
if (noAgent) {
mlutil.copyProperties(inputParams, agentOptions, [
'keepAliveMsecs', 'maxCachedSessions', 'maxFreeSockets', 'maxSockets', 'maxTotalSockets', 'scheduling', 'timeout', 'minVersion', 'maxVersion'
]);
connectionParams.agent = new https.Agent(agentOptions);
} else {
connectionParams.agent = inputParams.agent;
}
} else {
client.request = http.request;
if (noAgent) {
mlutil.copyProperties(inputParams, agentOptions, [
'keepAliveMsecs', 'maxFreeSockets', 'maxSockets', 'maxTotalSockets', 'scheduling', 'timeout'
]);
connectionParams.agent = new http.Agent(agentOptions);
} else {
connectionParams.agent = inputParams.agent;
}
}
client.internal = new internal.InternalClass(client);
}
function releaseClient(client) {
const agent = client.connectionParams.agent;
if (agent == null){
return;
}
agent.destroy();
client.connectionParams = null;
}
function setSliceMode(mode) {
if (mode !== 'array' && mode !== 'legacy') {
throw new Error('can only set slice mode to "array" or "legacy", not'+mode);
}
mlutil.setSliceMode(mode);
}
function prefLookup(hostname, options, callback) {
let isMultipleLookup = false;
if (options instanceof Function) {
callback = options;
} else if (options instanceof Object && options.all === true) {
isMultipleLookup = true;
}
// pass the preferred addresses or address to the callback
const prefCallback = isMultipleLookup ? callback : (err, addresses) => {
if (err) {
callback.call(null, err);
} else {
let addressdef = null;
// look for an ipv4 address
for (let i=0; i < addresses.length; i++) {
const candidate = addresses[i];
if (candidate.family === 4) {
addressdef = candidate;
break;
}
}
// fallback to first ipv6 address
if (addressdef === null) {
addressdef = addresses[0];
}
callback.call(null, null, addressdef.address, addressdef.family);
}
};
dns.lookup(hostname, {all:true, verbatim:false}, prefCallback);
}
module.exports = {
createDatabaseClient: MarkLogicClient,
/**
* A factory for creating a document query builder.
* @function marklogic.queryBuilder
* @since 1.0
* @returns {queryBuilder} a helper for defining a document query
*/
queryBuilder: queryBuilder.builder,
/**
* A factory for creating a cts query builder.
* @function marklogic.ctsQueryBuilder
* @since 2.7.0
* @returns {ctsQueryBuilder} a helper for defining a cts query
*/
ctsQueryBuilder: ctsQueryBuilder.builder,
/**
* A factory for creating a document patch builder
* @function marklogic.patchBuilder
* @since 1.0
* @returns {patchBuilder} a helper for defining a document patch
*/
patchBuilder: patchBuilder,
/**
* A factory for creating a values builder
* @function marklogic.valuesBuilder
* @since 1.0
* @returns {valuesBuilder} a helper for defining a query
* to project tuples (rows) of values from documents
*/
valuesBuilder: valuesBuilder.builder,
/**
* A factory for creating a rows plan builder.
* @function marklogic.planBuilder
* @since 2.4
* @returns {planBuilder} a helper for defining a rows query
*/
planBuilder: planBuilder.builder,
/**
* Configures the slice mode of the query builder and values builder
* to conform to Array.prototype.slice(begin, end) where begin is
* zero-based or legacy slice(pageStart, pageLength) where pageStart
* is one-based. The default is array slice mode. Legacy slice mode
* is deprecated and will be removed in the next major release.
* @function marklogic.setSliceMode
* @since 1.0
* @param {string} mode - "array" or "legacy"
*/
setSliceMode: setSliceMode,
/**
* Releases the client and destroys the agent.
* @function marklogic.releaseClient
* @since 3.0.0
* @param MarkLogicClient
*/
releaseClient: releaseClient
};