ml-modules.root.data-hub.data-services.mapping.generateMappingFunctions.mjs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of marklogic-data-hub Show documentation
Show all versions of marklogic-data-hub Show documentation
Library for Creating an Operational Data Hub on MarkLogic
'use strict';
import DataHubSingleton from "/data-hub/5/datahub-singleton.mjs";
import hubUtils from "/data-hub/5/impl/hub-utils.mjs";
xdmp.securityAssert("http://marklogic.com/data-hub/privileges/write-mapping", "execute");
const es = require("/MarkLogic/entity-services/entity-services.xqy");
const datahub = DataHubSingleton.instance();
const xqueryLib = require('/data-hub/5/builtins/steps/mapping/entity-services/xquery-lib.xqy');
function mlGenerateFunctionMetadata(uri) {
const match = new RegExp('^(.*).(sjs|mjs|xqy)$').exec(uri);
// The core.sjs/core.mjs is intended to be a pass through to support upgrades. Function Metadata is not generated so that there is no
// overlap with the core mapping functions written in XQuery. Mappings may not behave correctly as a result
if (!(match === null || uri === "/data-hub/5/mapping-functions/core.mjs" || uri === "/data-hub/5/mapping-functions/core.sjs" || uri.endsWith(".invoke.mjs"))) {
const uriVal = match[1];
const metadataXml = generateMetadata(uri);
const collection = 'http://marklogic.com/entity-services/function-metadata';
const permissions = xdmp.defaultPermissions().concat([
xdmp.permission(datahub.config.FLOWOPERATORROLE, 'execute'),
xdmp.permission(datahub.config.FLOWDEVELOPERROLE, 'execute'),
xdmp.permission(datahub.config.FLOWDEVELOPERROLE, 'update'),
xdmp.permission(datahub.config.FLOWOPERATORROLE, 'read'),
xdmp.permission(datahub.config.FLOWDEVELOPERROLE, 'read'),
xdmp.permission(datahub.consts.DATA_HUB_MODULE_READER_ROLE, 'execute'),
xdmp.permission(datahub.consts.DATA_HUB_MODULE_WRITER_ROLE, 'update'),
xdmp.permission(datahub.consts.DATA_HUB_MODULE_READER_ROLE, 'read'),
xdmp.permission("data-hub-module-reader", "execute"),
// In the absence of this, ML will report an error about standard-library.xqy not being found. This is misleading; the
// actual problem is that a mapping will fail if the XML or XSLT representation of a custom mapping function library
// does not have this permission on it, which is expected to be on every other DHF module.
xdmp.permission("rest-extension-user", "execute")
]);
const contentType = xdmp.uriContentType(uri);
const isSjs = contentType === "application/vnd.marklogic-javascript";
const isMjs = contentType === "application/vnd.marklogic-js-module";
const isJavaScript = isSjs || isMjs;
let writeInfo = fn.head(xdmp.invokeFunction(() => {
if (isJavaScript) {
const functionNames = metadataXml.xpath("*:function-def/@name ! string(.)").toArray();
if (functionNames.length > 0) {
const importObject = `{${functionNames.join(", ")}}`;
const importLine = isMjs ? `import ${importObject} from "${uri}";`: `const ${importObject} = require("${uri}");\n`;
const functionCalls = [];
for (const functionDef of metadataXml.xpath("*:function-def")) {
const functionName = fn.string(functionDef.xpath("@name"));
const parameterCalls = functionDef.xpath("*:parameters/*:parameter").toArray().map(param => {
const name = fn.string(param.xpath("@name"));
return `args["${name}"]`;
}).join(", ");
functionCalls.push(`"${functionName}": (args) => {
return ${functionName}(${parameterCalls});
}`);
}
const javaScriptModule = `${importLine}
const call = {${functionCalls.join(", ")}};
call[external.functionName](external.args);`;
const invokeUri = fn.replace(uri, "\\.[sm]js$", ".invoke.mjs");
hubUtils.writeDocument(
invokeUri,
new NodeBuilder().startDocument().addText(javaScriptModule).endDocument().toNode(),
permissions,
[],
datahub.config.MODULESDATABASE);
}
}
return hubUtils.writeDocument(uriVal + ".xml", metadataXml, permissions, [collection], datahub.config.MODULESDATABASE);
},
{update: "true"}));
if (writeInfo && fn.exists(writeInfo.transaction)) {
xqueryLib.functionMetadataPut(uriVal + ".xml");
} else {
datahub.debug.log({message: `No write for function metadata. (${xdmp.describe(writeInfo)})`, type: 'notice'});
}
}
}
function generateMetadata(uri) {
let metadataXml;
if (uri === "/data-hub/5/mapping-functions/core-functions.xqy") {
metadataXml = xqueryLib.functionMetadataGenerateWithNamespace("http://marklogic.com/data-hub/mapping/functions", uri);
}
// Custom XQuery mapping functions are required to have a URI starting with /custom-modules/mapping-functions and
// a namespace of http://marklogic.com/mapping-functions/custom
else if (fn.endsWith(uri, ".xqy")) {
metadataXml = xqueryLib.functionMetadataGenerateWithNamespace("http://marklogic.com/mapping-functions/custom", uri);
} else {
metadataXml = xqueryLib.functionMetadataGenerate(uri);
}
return addMapNamespaceToMetadata(es.functionMetadataValidate(metadataXml));
}
/**
* Ensures that the map namespace is declared in the XML mapping document. This ensures that it is carried over to the
* XSL stylesheet that is generated via es.mappingPut. And that ensures that the map:* references in the stylesheet
* generated for DHF's core.sjs module are resolved correctly, no matter the context.
*/
function addMapNamespaceToMetadata(xml) {
let metadata = xml;
try {
xml = xdmp.quote(xml).replace('', '');
let query = "let $xml := xdmp:unquote('" + xml + "')/node() return element {fn:node-name($xml)} {$xml/@*,namespace {'map'} {'http://marklogic.com/xdmp/map'},$xml/node()}";
metadata = xdmp.xqueryEval(query);
} catch (e) {
datahub.debug.log({
message: "Unable to add the map namespace prefix to the metadata document, which is intended to carryover to the " +
"compiled stylesheet. The compiled stylesheet may still function correctly if it does not reference any map:* functions.",
type: 'error',
stack: e.stack
});
}
return fn.head(metadata);
}
xdmp.invokeFunction(() => {
for (const uri of cts.uris(null, ["concurrent", "eager", "score-zero"], cts.directoryQuery(["/data-hub/5/mapping-functions/", "/custom-modules/mapping-functions/"], "infinity"))) {
mlGenerateFunctionMetadata(fn.string(uri));
}
}, {database: xdmp.modulesDatabase(), update: "true"});
© 2015 - 2024 Weber Informatics LLC | Privacy Policy