mirror of
https://gitlab.com/octospacc/octospacc.gitlab.io
synced 2025-06-05 21:59:15 +02:00
Update site structure, update global js to add domain warning, update SpiderADB
This commit is contained in:
164
static/Assets/Lib/Trasformapi.js
Normal file
164
static/Assets/Lib/Trasformapi.js
Normal file
@@ -0,0 +1,164 @@
|
||||
(function(){
|
||||
|
||||
// NOTE: using defiant.js gives us '[undefined]' arrays instead of '[]' ones sometimes, should be fixed
|
||||
|
||||
const Exp = {};
|
||||
let MakeTreeFromXml;
|
||||
|
||||
const platformIsNode = (typeof module === 'object' && typeof module.exports === 'object');
|
||||
const platformIsBrowser = (typeof window !== 'undefined' && typeof window.document !== 'undefined');
|
||||
|
||||
if (platformIsNode && !platformIsBrowser) {
|
||||
MakeTreeFromXml = (xml) => new require('jsdom').JSDOM(xml);
|
||||
// TODO load all other dependencies
|
||||
}
|
||||
|
||||
if (platformIsBrowser) {
|
||||
MakeTreeFromXml = (xml) => new DOMParser().parseFromString(xml, 'text/xml');
|
||||
}
|
||||
|
||||
Exp.Trasformapi = (transformerXml, initOptions={}) => {
|
||||
var transformerTree = MakeTreeFromXml(transformerXml);
|
||||
initOptions.sets ||= {};
|
||||
for (const attr of transformerTree.querySelector(':scope > set')?.attributes) {
|
||||
initOptions.sets[attr.name] = attr.value;
|
||||
}
|
||||
return {
|
||||
TransformForInput: (entityName, upstreamName, dataIn, transformOptions) => _TransformForInput(transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions),
|
||||
TransformForOutput: (entityName, upstreamName, dataIn, transformOptions) => _TransformForOutput(transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions),
|
||||
};
|
||||
}
|
||||
|
||||
function _TransformForInput (transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions={}) {
|
||||
const globalSets = { ...initOptions.sets, ...transformOptions.sets };
|
||||
// due to 2 bugs in defiant, we need to rename json keys
|
||||
// <https://stackoverflow.com/questions/68903102/renaming-object-keys-which-are-nested/68903897#68903897>
|
||||
function JsonObjectKeysFix (obj) {
|
||||
// TODO avoid collisions? (even if they're unlikely with what we're doing)
|
||||
return (obj !== undefined && obj !== null ? Object.fromEntries(Object.entries(obj).map( ([key,value]) => {
|
||||
key = key.replaceAll(':', '_'); // avoid "XML Parsing Error: prefix not bound to a namespace" on Firefox
|
||||
if (key.startsWith('@')) {
|
||||
// prefix any key starting with '@' with a character
|
||||
key = `_${key}`
|
||||
};
|
||||
return typeof value == "object"
|
||||
? [key, JsonObjectKeysFix(value)]
|
||||
: [key, value]
|
||||
})) : obj);
|
||||
}
|
||||
function MakeApiEntityObject (entityName, upstreamName, dataIn) {
|
||||
if (!dataIn) {
|
||||
// nothing to do
|
||||
return;
|
||||
};
|
||||
let dataOut = {};
|
||||
const entitySchema = transformerTree.querySelector(`:scope > entity[name="${entityName}"]`);
|
||||
for (const propSchema of entitySchema.querySelectorAll(':scope > prop')) {
|
||||
const propName = propSchema.getAttribute('name');
|
||||
const propType = propSchema.getAttribute('type').split('[]')[0];
|
||||
const propDataType = (propSchema.getAttribute('type').endsWith('[]') ? [] : {});
|
||||
const propContent = propSchema.querySelector(`:scope > content[upstream="${upstreamName}"]`);
|
||||
if (!propContent) {
|
||||
// property is not implemented for the current upstream, skip it
|
||||
continue;
|
||||
}
|
||||
const propContentChildren = propContent.querySelectorAll(`:scope > prop`);
|
||||
if (propContentChildren.length === 0) {
|
||||
const dataKey = SubstituteStringSets(propContent.getAttribute('query'), globalSets);
|
||||
const dataInContent = (dataIn instanceof Node
|
||||
? GetElementsByXPath(dataKey, dataIn)[0]?.textContent
|
||||
: (dataKey ? /*_.get*/defiant.search(dataIn, dataKey)[0] : dataIn)
|
||||
);
|
||||
// I don't know if this is fully correct
|
||||
if (Array.isArray(propDataType) && Array.isArray(dataInContent)) {
|
||||
for (const itemContent of dataInContent) {
|
||||
dataOut[propName] = (["string", "number", "int", "float"].includes(propType)
|
||||
? SetOrPush(itemContent, propDataType)
|
||||
: SetOrPush(MakeApiEntityObject(propType, upstreamName, itemContent), propDataType)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dataOut[propName] = (["string", "number", "int", "float"].includes(propType)
|
||||
? SetOrPush(dataInContent, propDataType)
|
||||
: SetOrPush(MakeApiEntityObject(propType, upstreamName, dataInContent), propDataType)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dataOut[propName] = {}; // NOTE: in some cases, this should be an array, I guess, or maybe not?
|
||||
for (const propChildSchema of propContentChildren) {
|
||||
const entityChildSchema = transformerTree.querySelector(`:scope > entity[name="${propType}"]`);
|
||||
const propChildName = propChildSchema.getAttribute('name');
|
||||
const propChildProp = entityChildSchema.querySelector(`:scope > prop[name="${propChildName}"]`);
|
||||
const propChildType = propChildProp.getAttribute('type').split('[]')[0];
|
||||
const propChildDataType = (propChildProp.getAttribute('type').endsWith('[]') ? [] : {});
|
||||
const childDataKey = SubstituteStringSets(propChildSchema.getAttribute('query'), globalSets);
|
||||
let childDataInContent = [];
|
||||
if (dataIn instanceof Node) {
|
||||
const nodes = GetElementsByXPath(childDataKey, dataIn);
|
||||
if (nodes.length === 1) {
|
||||
childDataInContent = nodes[0]?.textContent
|
||||
} else {
|
||||
for (const node of nodes) {
|
||||
childDataInContent.push(node?.textContent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
childDataInContent = (childDataKey ? /*_.get*/defiant.search(dataIn, childDataKey)[0] : dataIn);
|
||||
}
|
||||
const childResult = (["string", "number", "int", "float"].includes(propChildType)
|
||||
? childDataInContent
|
||||
: MakeApiEntityObject(propChildType, upstreamName, childDataInContent)
|
||||
);
|
||||
if (Array.isArray(propDataType)) {
|
||||
if (!Array.isArray(dataOut[propName])) {
|
||||
dataOut[propName] = [];
|
||||
}
|
||||
const childItems = SureArray(childResult);
|
||||
for (const childItemIndex in childItems) {
|
||||
const childItem = childItems[childItemIndex];
|
||||
if (!dataOut[propName][childItemIndex]) {
|
||||
dataOut[propName][childItemIndex] = {};
|
||||
}
|
||||
dataOut[propName][childItemIndex][propChildName] = childItem;
|
||||
}
|
||||
} else {
|
||||
dataOut[propName][propChildName] = childResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataOut;
|
||||
}
|
||||
return MakeApiEntityObject (entityName, upstreamName, (dataIn instanceof Node ? dataIn : JsonObjectKeysFix(dataIn)));
|
||||
}
|
||||
|
||||
function _TransformForOutput (transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions={}) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// <https://stackoverflow.com/questions/36303869/how-to-use-document-evaluate-and-xpath-to-get-a-list-of-elements/42600459#42600459>
|
||||
// TODO: 'document' won't work on nodejs, must change it
|
||||
function GetElementsByXPath (xpath, parent) {
|
||||
let results = [];
|
||||
let query = (parent?.getRootNode() || document).evaluate(xpath, (parent || document), ((ns) => ns), XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
||||
for (let i=0, length=query.snapshotLength; i<length; ++i) {
|
||||
results.push(query.snapshotItem(i));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const SetOrPush = (item, dest) => (Array.isArray(dest) ? [...dest, item] : item);
|
||||
|
||||
const SureArray = (item) => (Array.isArray(item) ? item : [item]);
|
||||
|
||||
const SubstituteStringSets = (string, sets) => {
|
||||
for (const set in sets) {
|
||||
string = string?.replaceAll(`{${set}}`, sets[set]);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
if (platformIsNode) module.exports = Exp;
|
||||
if (platformIsBrowser) window.Trasformapi = Exp.Trasformapi;
|
||||
|
||||
})();
|
Reference in New Issue
Block a user