import async from 'async';
/**
* Class with methods for spread and unspread of the spreadGraph on pathGraph(s).
*
* @class
* @description `import { GraphSpreading } from 'ancient-graph-spreading';`
*/
class GraphSpreading {
/**
* @param {SpreadGraph} spreadGraph
*/
constructor(spreadGraph) {
this.spreadGraph = spreadGraph;
this.pathGraphs = [];
}
/**
* Custom async callbacks support
*
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {function} iteratee - A function to apply to each item in coll.
* @param {function} callback - A callback which is called when all iteratee functions have finished.
*/
each(coll, iteratee, callback) {
async.each(coll, iteratee, callback);
}
/**
* @param {PathGraph} pathGraph
*/
addPathGraph(pathGraph) {
this.pathGraphs.push(pathGraph);
}
/**
* Custom wrapper of query to spread graph
*
* @param {Object} [query]
* @param {Object} [context]
*/
_wrapSpreadQuery(query, context) {}
/**
* Custom wrapper of query to path graph
*
* @param {Object} [query]
* @param {Object} [pathGraph]
* @param {String} [fromField]
* @param {String} [toField]
* @param {Object} [context]
*/
_wrapPathQuery(query, pathGraph, fromField, toField, context) {}
/**
* Custom getter of possible from fields.
*
* @param {Object} [pathGraph]
* @param {Object} [pathLink]
* @param {Object} [spreadLink]
* @return {String[]} [fromFields]
*/
_getFromFields(pathGraph, pathLink, spreadLink) {
return pathGraph.fromFields;
}
/**
* Custom getter of possible to fields.
*
* @param {Object} [pathGraph]
* @param {Object} [pathLink]
* @param {Object} [spreadLink]
* @return {String[]} [toFields]
*/
_getToFields(pathGraph, pathLink, spreadLink) {
return pathGraph.toFields;
}
/**
* Spread by pathLink and specified fromField with available spreadLinks.
*
* @param {string} fromField
* @param {PathGraph} pathGraph
* @param {PathLink} pathLink
* @param {Object} [context]
* @param {GraphSpreading~spreadFromSpreadLinkByPathLinkCallback} [handler]
* @param {GraphSpreading~spreadByPathLinkCallback} [callback]
*/
_spreadByPathLink(fromField, pathGraph, pathLink, context, handler, callback) {
var query = {
[this.spreadGraph.config.aliases[this.spreadGraph.variableField]]: pathLink[pathGraph.config.aliases[fromField]],
};
this._wrapSpreadQuery(query, context);
this.spreadGraph.fetch(query, undefined, (error, spreadLinks) => {
if (spreadLinks.length) {
this.each(spreadLinks, (spreadLink, next) => {
this.spreadFromSpreadLinkByPathLink(spreadLink, pathGraph, pathLink, context, handler, () => { next(); });
}, () => {
if (callback) callback();
});
} else {
if (callback) callback();
}
});
}
/**
* Spread by pathLink with available spreadLinks.
*
* @param {PathGraph} pathGraph
* @param {PathLink} pathLink
* @param {Object} [context]
* @param {GraphSpreading~spreadFromSpreadLinkByPathLinkCallback} [handler]
* @param {GraphSpreading~spreadByPathLinkCallback} [callback]
*/
spreadByPathLink(pathGraph, pathLink, context, handler, callback) {
this.each(this._getFromFields(pathGraph, pathLink), (fromField, next) => {
this._spreadByPathLink(fromField, pathGraph, pathLink, context, handler, next);
}, callback);
}
/**
* Optional callback.
*
* @callback GraphSpreading~spreadByPathLinkCallback
*/
/**
* Spread root of tree spreadLink.
*
* @param {SpreadLink} newSpreadLink
* @param {Object} [context]
* @param {Graph~insertCallback} [callback]
*/
spreadNewSpreadLink(newSpreadLink, context, callback) {
this.spreadGraph._spreadingHandler(undefined, undefined, undefined, newSpreadLink, context, (newSpreadLink) => {
if (newSpreadLink) {
this.spreadGraph.insert(newSpreadLink, callback, context);
} else {
if (callback) callback();
}
});
}
/**
* Spread by all available paths from spreadLink.
*
* @param {SpreadLink} spreadLink
* @param {Object} [context]
* @param {GraphSpreading~spreadFromSpreadLinkByPathGraphHandler} [handler]
* @param {GraphSpreading~spreadFromSpreadLinkByPathGraphCallback} [callback]
*/
spreadFromSpreadLink(spreadLink, context, handler, callback) {
this.each(this.pathGraphs, (pathGraph, next) => {
this.spreadFromSpreadLinkByPathGraph(spreadLink, pathGraph, context, handler, next);
}, () => {
if (callback) callback();
});
}
/**
* Spread by all available paths in pathGraph from spreadLink and specified fromField.
*
* @param {string} fromField
* @param {SpreadLink} spreadLink
* @param {PathGraph} pathGraph
* @param {Object} [context]
* @param {GraphSpreading~spreadFromSpreadLinkByPathGraphHandler} [handler]
* @param {GraphSpreading~spreadFromSpreadLinkByPathGraphCallback} [callback]
*/
_spreadFromSpreadLinkByPathGraph(fromField, spreadLink, pathGraph, context, handler, callback) {
var query = {
[pathGraph.config.aliases[fromField]]: spreadLink[this.spreadGraph.variableField],
};
this._wrapPathQuery(query, pathGraph, fromField, undefined, context);
pathGraph.fetch(query, undefined, (error, pathLinks) => {
this.each(pathLinks, (pathLink, next) => {
this.spreadFromSpreadLinkByPathLink(spreadLink, pathGraph, pathLink, context, handler, next);
}, () => {
if (callback) callback();
});
});
}
/**
* Spread by all available paths in pathGraph from spreadLink.
*
* @param {SpreadLink} spreadLink
* @param {PathGraph} pathGraph
* @param {GraphSpreading~spreadFromSpreadLinkByPathGraphHandler} [handler]
* @param {GraphSpreading~spreadFromSpreadLinkByPathGraphCallback} [callback]
*/
spreadFromSpreadLinkByPathGraph(spreadLink, pathGraph, context, handler, callback) {
this.each(this._getFromFields(pathGraph, undefined, spreadLink), (fromField, next) => {
this._spreadFromSpreadLinkByPathGraph(fromField, spreadLink, pathGraph, context, handler, next);
}, callback);
}
/**
* Optional handler. If present, called with an error object as the first argument and, if no error, others arguments with results of spreading.
*
* @callback GraphSpreading~spreadFromSpreadLinkByPathGraphHandler
* @param {Error} [error]
* @param {string} [newSpreadLinkId]
* @param {SpreadLink} [prevSpreadLink]
* @param {PathGraph} [pathGraph]
* @param {PathLink} [pathLink]
*/
/**
* Optional callback.
*
* @callback GraphSpreading~spreadFromSpreadLinkByPathGraphCallback
*/
/**
* Spread by pathLink and specified toField in pathGraph from spreadLink.
*
* @param {SpreadLink} spreadLink
* @param {PathGraph} pathGraph
* @param {PathLink} pathLink
* @param {Object} [context]
* @param {GraphSpreading~spreadFromSpreadLinkByPathLinkCallback} [callback]
*/
_spreadFromSpreadLinkByPathLink(toField, spreadLink, pathGraph, pathLink, context, callback) {
this.spreadGraph._spreadingHandler(spreadLink, pathGraph, pathLink, {
[this.spreadGraph.config.aliases[this.spreadGraph.constantField]]: spreadLink[this.spreadGraph.config.aliases[this.spreadGraph.constantField]],
[this.spreadGraph.config.aliases[this.spreadGraph.variableField]]: pathLink[pathGraph.config.aliases[toField]],
[this.spreadGraph.config.aliases.prev]: spreadLink[this.spreadGraph.config.aliases.id],
[this.spreadGraph.config.aliases.path]: pathLink[pathGraph.config.aliases.id],
[this.spreadGraph.config.aliases.root]: spreadLink[this.spreadGraph.config.aliases.root]?spreadLink[this.spreadGraph.config.aliases.root]:spreadLink[this.spreadGraph.config.aliases.id]
}, context, (newSpreadLink) => {
if (newSpreadLink) {
this.spreadGraph.insert(newSpreadLink, (error, id) => {
if (callback) callback(error, id, spreadLink, pathGraph, pathLink);
}, context);
} else {
if (callback) callback();
}
});
}
/**
* Spread by pathLink in pathGraph from spreadLink.
*
* @param {SpreadLink} spreadLink
* @param {PathGraph} pathGraph
* @param {PathLink} pathLink
* @param {Object} [context]
* @param {GraphSpreading~spreadFromSpreadLinkByPathLinkHandler} [handler]
* @param {GraphSpreading~spreadFromSpreadLinkByPathLinkCallback} [callback]
*/
spreadFromSpreadLinkByPathLink(spreadLink, pathGraph, pathLink, context, handler, callback) {
this.each(this._getToFields(pathGraph, pathLink, spreadLink), (toField, next) => {
this._spreadFromSpreadLinkByPathLink(toField, spreadLink, pathGraph, pathLink, context, (error, id, prev, pathGraph, pathLink) => {
if (handler) handler(error, id, prev, pathGraph, pathLink);
next();
});
}, callback);
}
/**
* Optional handler.
*
* @callback GraphSpreading~spreadFromSpreadLinkByPathLinkCallback
* @param {Error} [error]
* @param {string} [newSpreadLinkId]
* @param {SpreadLink} [prevSpreadLink]
* @param {PathGraph} [pathGraph]
* @param {PathLink} [pathLink]
*/
/**
* Optional callback. If present, called with an error object as the first argument and, if no error, the unique id of inserted spread link as the second.
*
* @callback GraphSpreading~spreadFromSpreadLinkByPathLinkCallback
*/
/**
* Remove spreadLinks with specific prev spreadLink id.
*
* @param {string} spreadLinkId
* @param {Object} [context]
* @param {GraphSpreading~unspreadFromRemovedSpreadLinkByPrevIdHandler} [handler]
* @param {GraphSpreading~unspreadFromRemovedSpreadLinkByPrevIdCallback} [callback]
*/
unspreadFromRemovedSpreadLinkByPrevId(spreadLinkId, context, handler, callback) {
var query = {
[this.spreadGraph.config.aliases.prev]: spreadLinkId,
};
this._wrapSpreadQuery(query, context);
if (handler) {
this.spreadGraph.fetch(query, undefined, (error, spreadLinks) => {
if (error) {
if (callback) callback(error);
} else {
this.each(spreadLinks, (spreadLink, next) => {
this.spreadGraph.remove(spreadLink[this.spreadGraph.config.aliases.id], (error, count) => {
handler(error, spreadLink);
next();
}, context);
}, () => {
if (callback) callback(undefined, spreadLinks.length);
});
}
});
} else {
this.spreadGraph.remove(query, callback, context);
}
}
/**
* Optional handler. If present, called with an error object as the first argument and, if no error, others arguments with results of unspreading.
*
* @callback GraphSpreading~unspreadFromRemovedSpreadLinkByPrevIdHandler
* @param {Error} [error]
* @param {SpreadLink} [spreadLink]
*/
/**
* Optional callback.
*
* @callback GraphSpreading~unspreadFromRemovedSpreadLinkByPrevIdCallback
* @param {Error} [error]
* @param {number} [count]
*/
/**
* Remove spreadLinks with specific path pathLink id.
*
* @param {string} pathLinkId
* @param {Object} [context]
* @param {GraphSpreading~unspreadByPathIdHandler} [handler]
* @param {GraphSpreading~unspreadByPathIdCallback} [callback]
*/
unspreadByPathId(pathLinkId, context, handler, callback) {
var query = {
[this.spreadGraph.config.aliases.path]: pathLinkId,
};
this._wrapSpreadQuery(query, context);
if (handler) {
this.spreadGraph.fetch(query, undefined, (error, spreadLinks) => {
if (error) {
if (callback) callback(error);
} else {
this.each(spreadLinks, (spreadLink, next) => {
this.spreadGraph.remove(spreadLink[this.spreadGraph.config.aliases.id], (error, count) => {
handler(error, spreadLink);
next();
}, context);
}, () => {
if (callback) callback(undefined, spreadLinks.length);
});
}
});
} else {
this.spreadGraph.remove(query, callback, context);
}
}
/**
* Optional handler.
*
* @callback GraphSpreading~unspreadByPathIdHandler
* @param {Error} [error]
* @param {SpreadLink} [spreadLink]
*/
/**
* Optional callback.
*
* @callback GraphSpreading~unspreadByPathIdCallback
* @param {Error} [error]
* @param {number} [count]
*/
/**
* Unspread all valid spreadLinks to this id.
*
* @param {string} id
* @param {Object} [context]
* @param {GraphSpreading~unspreadToHandler} [handler]
* @param {GraphSpreading~unspreadToCallback} [callback]
*/
unspread(id, context, handler, callback) {
var query = {
[this.spreadGraph.config.aliases[this.spreadGraph.variableField]]: id,
};
this._wrapSpreadQuery(query, context);
this.spreadGraph.fetch(query, undefined, (error, spreadLinks) => {
if (error) {
if (callback) callback(error);
} else {
this.each(spreadLinks, (spreadLink, next) => {
this.spreadGraph._unspreadingHandler(spreadLink, context, (permission) => {
if (permission) {
this.spreadGraph.remove(spreadLink[this.spreadGraph.config.aliases.id], (error, count) => {
if (handler) handler(error, spreadLink);
next();
}, context);
}
});
}, () => {
if (callback) callback(undefined, spreadLinks.length);
});
}
});
}
/**
* Optional handler.
*
* @callback GraphSpreading~unspreadToHandler
* @param {Error} [error]
* @param {SpreadLink} [spreadLink]
*/
/**
* Optional callback.
*
* @callback GraphSpreading~unspreadToCallback
*/
/**
* Spread all spread links from all available paths to this id.
*
* @param {string} id
* @param {Object} [context]
* @param {GraphSpreading~spreadToHandler} [handler]
* @param {GraphSpreading~spreadToCallback} [callback]
*/
spreadTo(id, context, handler, callback) {
this.each(this.pathGraphs, (pathGraph, nextPathGraph) => {
this.each(this._getToFields(pathGraph), (toField, nextToField) => {
var query = {
[this.spreadGraph.config.aliases[toField]]: id,
};
this._wrapPathQuery(query, pathGraph, undefined, toField, context);
pathGraph.fetch(query, undefined, (error, pathLinks) => {
this.each(pathLinks, (pathLink, nextPathLink) => {
this.spreadByPathLink(pathGraph, pathLink, context, handler, nextPathLink);
}, function(error) {
nextToField();
});
});
}, nextPathGraph);
}, () => {
if (callback) callback();
});
}
/**
* Optional handler. Fires after each processed spread link.
* Id can be empty if the `this.spreadGraph._spreadingHandler` banned spreading.
*
* @callback GraphSpreading~spreadToHandler
* @param {Error} [error]
* @param {string} [newSpreadLinkId]
* @param {SpreadLink} [prevSpreadLink]
* @param {PathGraph} [pathGraph]
* @param {PathLink} [pathLink]
*/
/**
* Optional callback.
*
* @callback GraphSpreading~spreadToCallback
*/
}
export { GraphSpreading };