fiftyone.pipeline.engines
Advanced tools
Comparing version 4.1.0-beta.15 to 4.1.0
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,83 +17,65 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
let require51 = (requestedPackage) => { | ||
try { | ||
return require(__dirname + "/../" + requestedPackage) | ||
} catch (e) { | ||
return require(requestedPackage); | ||
} | ||
} | ||
const require51 = (requestedPackage) => { | ||
try { | ||
return require(__dirname + '/../' + requestedPackage); | ||
} catch (e) { | ||
return require(requestedPackage); | ||
} | ||
}; | ||
const elementData = require51("fiftyone.pipeline.core").elementData; | ||
const missingPropertyServiceBase = require("./missingPropertyService"); | ||
const ElementData = require51('fiftyone.pipeline.core').ElementData; | ||
const MissingPropertyServiceBase = require('./missingPropertyService'); | ||
class aspectData extends elementData { | ||
/** | ||
class AspectData extends ElementData { | ||
/** | ||
* Extension of elementData which allows for a missing property service to be called when an accessed property isn't available. engines, an extension of flowElements also allow a restricted property list so certain properties can be excluded | ||
* @param {Object} options | ||
* @param {flowElement} options.flowElement | ||
* @param {missingPropertyService} options.missingPropertyService | ||
* @param {FlowElement} options.flowElement | ||
* @param {MissingPropertyService} options.missingPropertyService | ||
*/ | ||
constructor( | ||
{ | ||
flowElement, missingPropertyService = new missingPropertyServiceBase() | ||
}) { | ||
constructor ( | ||
{ | ||
flowElement, missingPropertyService = new MissingPropertyServiceBase() | ||
}) { | ||
super(...arguments); | ||
super(...arguments); | ||
if (missingPropertyService) { | ||
this.missingPropertyService = missingPropertyService; | ||
} | ||
if (missingPropertyService) { | ||
this.missingPropertyService = missingPropertyService; | ||
} | ||
} | ||
/** | ||
/** | ||
* The aspectData getter runs a series of actions if a property has / has not been found. If it hasn't been found it runs the missing property service if the property is referenced by the flowElement/engine. If the property is found a further check to see if it is restricted by a list passed into the flowElement/engine. | ||
* @param {String} key | ||
*/ | ||
get(key) { | ||
get (key) { | ||
let result; | ||
let result; | ||
try { | ||
result = this.getInternal(key); | ||
try { | ||
if (typeof result === 'undefined') { | ||
return this.missingPropertyService.check(key, this.flowElement); | ||
} | ||
} catch (e) { | ||
return this.missingPropertyService.check(key, this.flowElement); | ||
} | ||
result = this.getInternal(key); | ||
if (typeof result === "undefined") { | ||
return this.missingPropertyService.check(key, this.flowElement); | ||
} | ||
} catch (e) { | ||
return this.missingPropertyService.check(key, this.flowElement); | ||
} | ||
if (this.flowElement.restrictedProperties) { | ||
if (!this.flowElement.restrictedProperties.includes(key)) { | ||
throw "Property " + key + " was excluded from " + this.flowElement.dataKey; | ||
} | ||
} | ||
return result; | ||
if (this.flowElement.restrictedProperties) { | ||
if (!this.flowElement.restrictedProperties.includes(key)) { | ||
throw 'Property ' + key + ' was excluded from ' + this.flowElement.dataKey; | ||
} | ||
} | ||
return result; | ||
} | ||
} | ||
module.exports = aspectData; | ||
module.exports = AspectData; |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,36 +17,30 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
const aspectData = require("./aspectData"); | ||
const AspectData = require('./aspectData'); | ||
class aspectDataDictionary extends aspectData { | ||
/** | ||
class AspectDataDictionary extends AspectData { | ||
/** | ||
* Extension of elementDataDictionary which stores a {key,value} dictionary of elements like elementDataDictionary but with the additional aspectData extensions | ||
* @param {Object} options | ||
* @param {flowElement} options.flowElement | ||
* @param {missingPropertyService} options.missingPropertyService | ||
* @param {FlowElement} options.flowElement | ||
* @param {MissingPropertyService} options.missingPropertyService | ||
* @param {Object} options.contents | ||
*/ | ||
constructor({ flowElement, contents, missingPropertyService }) { | ||
constructor ({ flowElement, contents, missingPropertyService }) { | ||
super(...arguments); | ||
super(...arguments); | ||
this.contents = contents; | ||
} | ||
this.contents = contents; | ||
} | ||
getInternal(key) { | ||
return this.contents[key]; | ||
} | ||
getInternal (key) { | ||
return this.contents[key]; | ||
} | ||
} | ||
module.exports = aspectDataDictionary; | ||
module.exports = AspectDataDictionary; |
106
dataFile.js
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,75 +17,61 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
let os = require("os"); | ||
const os = require('os'); | ||
class dataFile { | ||
class DataFile { | ||
constructor ({ flowElement, identifier, updateURLParams, tempDataDirectory = os.tmpdir(), createTempDataCopy = true, data, path, autoUpdate = true, fileSystemWatcher = true, pollingInterval = 30, updateTimeMaximumRandomisation = 10, verifyMD5 = true, decompress = true, download = true, getDatePublished, getNextUpdate, verifyIfModifiedSince = true, updateOnStart = false, isRegistered = false, refresh }) { | ||
this.flowElement = flowElement; | ||
this.identifier = identifier; | ||
this.updateURLParams = updateURLParams; | ||
this.tempDataDirectory = tempDataDirectory; | ||
this.createTempDataCopy = createTempDataCopy; | ||
this.data = data; | ||
this.path = path; | ||
this.autoUpdate = autoUpdate; | ||
this.fileSystemWatcher = fileSystemWatcher; | ||
this.pollingInterval = pollingInterval; | ||
this.updateTimeMaximumRandomisation = updateTimeMaximumRandomisation; | ||
this.verifyMD5 = verifyMD5; | ||
this.decompress = decompress; | ||
this.download = download; | ||
this.getDatePublished = getDatePublished; | ||
this.getNextUpdate = getNextUpdate; | ||
this.verifyIfModifiedSince = verifyIfModifiedSince; | ||
this.updateOnStart = updateOnStart; | ||
this.isRegistered = isRegistered; | ||
constructor({ flowElement, identifier, updateURLParams, tempDataDirectory = os.tmpdir(), createTempDataCopy = true, data, path, autoUpdate = true, fileSystemWatcher = true, pollingInterval = 30, updateTimeMaximumRandomisation = 10, verifyMD5 = true, decompress = true, download = true, getDatePublished, getNextUpdate, verifyIfModifiedSince = true, updateOnStart = false, isRegistered = false, refresh }) { | ||
this.flowElement = flowElement; | ||
this.identifier = identifier; | ||
this.updateURLParams = updateURLParams; | ||
this.tempDataDirectory = tempDataDirectory; | ||
this.createTempDataCopy = createTempDataCopy; | ||
this.data = data; | ||
this.path = path; | ||
this.autoUpdate = autoUpdate; | ||
this.fileSystemWatcher = fileSystemWatcher; | ||
this.pollingInterval = pollingInterval; | ||
this.updateTimeMaximumRandomisation = updateTimeMaximumRandomisation; | ||
this.verifyMD5 = verifyMD5; | ||
this.decompress = decompress; | ||
this.download = download; | ||
this.getDatePublished = getDatePublished; | ||
this.getNextUpdate = getNextUpdate; | ||
this.verifyIfModifiedSince = verifyIfModifiedSince; | ||
this.updateOnStart = updateOnStart; | ||
this.isRegistered = isRegistered; | ||
if (refresh) { | ||
this.refresh = refresh; | ||
} | ||
if (path) { | ||
this.path = path; | ||
} else if (data) { | ||
this.data = data; | ||
} | ||
// Flag for whether the datafile is currently being updated | ||
this.updating = false; | ||
if (refresh) { | ||
this.refresh = refresh; | ||
} | ||
refresh(identifier) { | ||
this.flowElement.refresh(identifier); | ||
if (path) { | ||
this.path = path; | ||
} else if (data) { | ||
this.data = data; | ||
} | ||
get updateUrl() { | ||
// Flag for whether the datafile is currently being updated | ||
this.updating = false; | ||
} | ||
return this.urlFormatter(); | ||
refresh (identifier) { | ||
this.flowElement.refresh(identifier); | ||
} | ||
} | ||
get updateUrl () { | ||
return this.urlFormatter(); | ||
} | ||
urlFormatter() { | ||
// by default just return the url | ||
return this.updateURLParams.url; | ||
} | ||
urlFormatter () { | ||
// by default just return the url | ||
return this.updateURLParams.url; | ||
} | ||
} | ||
module.exports = dataFile; | ||
module.exports = DataFile; |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,317 +17,236 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
const fs = require("fs"); | ||
const zlib = require("zlib"); | ||
const crypto = require("crypto"); | ||
const https = require("https"); | ||
const http = require("http"); | ||
const url = require("url"); | ||
const fs = require('fs'); | ||
const zlib = require('zlib'); | ||
const crypto = require('crypto'); | ||
const https = require('https'); | ||
const http = require('http'); | ||
const url = require('url'); | ||
const minToMs = (min) => min * 60000; | ||
class dataFileUpdateService { | ||
class DataFileUpdateService { | ||
constructor (pipeline) { | ||
this.pipeline = pipeline; | ||
} | ||
constructor(pipeline) { | ||
updateDataFile (dataFile) { | ||
const dataFileUpdateService = this; | ||
this.pipeline = pipeline; | ||
if (dataFile.updating) { | ||
return false; | ||
} else { | ||
dataFile.updating = true; | ||
} | ||
updateDataFile(dataFile) { | ||
const urlParts = url.parse(dataFile.updateUrl); | ||
let dataFileUpdateService = this; | ||
const requestOptions = urlParts; | ||
if (dataFile.updating) { | ||
if (dataFile.verifyIfModifiedSince) { | ||
try { | ||
requestOptions.headers = { | ||
'If-Modified-Since': dataFile.getDatePublished() | ||
}; | ||
} catch (e) { | ||
return false; | ||
// getPublished might not exist if no datafile | ||
} else { | ||
} | ||
} | ||
dataFile.updating = true; | ||
let request; | ||
} | ||
if (urlParts.protocol === 'https:') { | ||
request = https.get(requestOptions); | ||
} else { | ||
request = http.get(requestOptions); | ||
} | ||
let urlParts = url.parse(dataFile.updateUrl); | ||
request.on('response', function (response) { | ||
if (response.statusCode !== 200) { | ||
dataFile.updating = false; | ||
let requestOptions = urlParts; | ||
if (dataFile.verifyIfModifiedSince) { | ||
try { | ||
requestOptions.headers = { | ||
'If-Modified-Since': dataFile.getDatePublished(), | ||
} | ||
} catch (e) { | ||
// getPublished might not exist if no datafile | ||
} | ||
} | ||
let request; | ||
if (urlParts.protocol === "https:") { | ||
request = https.get(requestOptions); | ||
} else { | ||
request = http.get(requestOptions); | ||
} | ||
request.on("response", function (response) { | ||
if (response.statusCode !== 200) { | ||
dataFile.updating = false; | ||
switch (response.statusCode) { | ||
case (429): | ||
dataFileUpdateService.pipeline.log("error", "Too many requests to '" + dataFile.updateUrl + "' for engine '" + | ||
switch (response.statusCode) { | ||
case (429): | ||
dataFileUpdateService.pipeline.log('error', "Too many requests to '" + dataFile.updateUrl + "' for engine '" + | ||
dataFile.flowElement.dataKey + "'"); | ||
break; | ||
case (304): | ||
dataFileUpdateService.pipeline.log("warn", "No data update available from " + dataFile.updateUrl + "' for engine '" + | ||
break; | ||
case (304): | ||
dataFileUpdateService.pipeline.log('warn', 'No data update available from ' + dataFile.updateUrl + "' for engine '" + | ||
dataFile.flowElement.dataKey + "'"); | ||
break; | ||
case (403): | ||
dataFileUpdateService.pipeline.log("error", "Access denied from " + dataFile.updateUrl + "' for engine '" + | ||
break; | ||
case (403): | ||
dataFileUpdateService.pipeline.log('error', 'Access denied from ' + dataFile.updateUrl + "' for engine '" + | ||
dataFile.flowElement.dataKey + "'"); | ||
break; | ||
default: | ||
dataFileUpdateService.pipeline.log("error", "Error" + response.statusCode + " from " + dataFile.updateUrl + "' for engine '" + | ||
break; | ||
default: | ||
dataFileUpdateService.pipeline.log('error', 'Error' + response.statusCode + ' from ' + dataFile.updateUrl + "' for engine '" + | ||
dataFile.flowElement.dataKey + "'"); | ||
break; | ||
} | ||
break; | ||
} | ||
dataFileUpdateService.checkNextUpdate(dataFile); | ||
dataFileUpdateService.checkNextUpdate(dataFile); | ||
return false; | ||
return false; | ||
} | ||
} | ||
const filename = dataFile.tempDataDirectory + '/' + dataFile.identifier + Date.now(); | ||
let filename = dataFile.tempDataDirectory + "/" + dataFile.identifier + Date.now(); | ||
response.pipe(fs.createWriteStream(filename)); | ||
response.pipe(fs.createWriteStream(filename)); | ||
response.on('end', function () { | ||
// Open file | ||
response.on("end", function () { | ||
if (dataFile.verifyMD5) { | ||
const headerMD5 = response.headers['content-md5']; | ||
// Open file | ||
var fd = fs.createReadStream(filename); | ||
if (dataFile.verifyMD5) { | ||
var hash = crypto.createHash('md5'); | ||
hash.setEncoding('hex'); | ||
let headerMD5 = response.headers["content-md5"]; | ||
fd.on('end', function () { | ||
hash.end(); | ||
if (hash.read() !== headerMD5) { | ||
dataFileUpdateService.pipeline.log('error', "MD5 doesn't match from '" + dataFile.updateUrl + "' for engine '" + dataFile.flowElement.dataKey + "'"); | ||
var fd = fs.createReadStream(filename); | ||
dataFile.updating = false; | ||
dataFileUpdateService.checkNextUpdate(dataFile); | ||
} else { | ||
dataFileUpdateService.processFile(dataFile, filename); | ||
} | ||
}); | ||
var hash = crypto.createHash('md5'); | ||
hash.setEncoding('hex'); | ||
fd.pipe(hash); | ||
} else { | ||
dataFileUpdateService.processFile(dataFile, filename); | ||
} | ||
}); | ||
}); | ||
} | ||
fd.on('end', function () { | ||
hash.end(); | ||
if (hash.read() !== headerMD5) { | ||
fileReady (dataFile, filename) { | ||
const dataFileUpdateService = this; | ||
dataFileUpdateService.pipeline.log("error", "MD5 doesn't match from '" + dataFile.updateUrl + "' for engine '" + dataFile.flowElement.dataKey + "'"); | ||
fs.readFile(filename, function (err, data) { | ||
if (err) { | ||
dataFileUpdateService.pipeline.log('error', err); | ||
} | ||
dataFile.updating = false; | ||
dataFileUpdateService.checkNextUpdate(dataFile); | ||
fs.writeFile(dataFile.path, data, function (err) { | ||
if (err) { | ||
dataFileUpdateService.pipe.log('error', err); | ||
} | ||
} else { | ||
dataFile.refresh(); | ||
dataFileUpdateService.processFile(dataFile, filename); | ||
dataFileUpdateService.checkNextUpdate(dataFile); | ||
} | ||
}); | ||
dataFile.updating = false; | ||
fd.pipe(hash); | ||
} else { | ||
dataFileUpdateService.processFile(dataFile, filename); | ||
} | ||
}); | ||
// Delete the temp file | ||
fs.unlink(filename, function (err) { | ||
if (err) { | ||
dataFileUpdateService.pipeline.log('error', err); | ||
} | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
processFile (dataFile, filename) { | ||
const dataFileUpdateService = this; | ||
fileReady(dataFile, filename) { | ||
if (dataFile.decompress) { | ||
fs.readFile(filename, function (err, buffer) { | ||
if (err) { | ||
dataFileUpdateService.pipeline.log('error', err); | ||
} | ||
let dataFileUpdateService = this; | ||
zlib.gunzip(buffer, function (err, data) { | ||
if (err) { | ||
dataFileUpdateService.pipeline.log('error', err); | ||
} | ||
fs.readFile(filename, function (err, data) { | ||
const doneFileName = filename + '_done'; | ||
fs.writeFile(dataFile.path, data, function (err) { | ||
if (err) { | ||
dataFileUpdateService.pipe.log("error", err); | ||
} | ||
dataFile.refresh(); | ||
dataFileUpdateService.checkNextUpdate(dataFile); | ||
dataFile.updating = false; | ||
// Delete the temp file | ||
fs.unlink(filename, function (err) { | ||
if (err) { | ||
dataFileUpdateService.pipeline.log("error", err); | ||
} | ||
}); | ||
}); | ||
fs.writeFile(doneFileName, data, function () { | ||
dataFileUpdateService.fileReady(dataFile, doneFileName); | ||
}); | ||
}); | ||
}); | ||
} else { | ||
dataFileUpdateService.fileReady(dataFile, filename); | ||
} | ||
} | ||
processFile(dataFile, filename) { | ||
checkNextUpdate (dataFile) { | ||
try { | ||
const dataFileUpdateService = this; | ||
let dataFileUpdateService = this; | ||
let interval = minToMs((Math.floor(Math.random() * dataFile.updateTimeMaximumRandomisation) + 1) + dataFile.pollingInterval); | ||
if (dataFile.decompress) { | ||
interval += dataFile.getNextUpdate().getMilliseconds(); | ||
fs.readFile(filename, function (err, buffer) { | ||
// Run update on start if specified to do so | ||
if (dataFile.updateOnStart) { | ||
dataFileUpdateService.updateDataFile(dataFile); | ||
} else { | ||
setTimeout(function () { | ||
dataFileUpdateService.updateDataFile(dataFile); | ||
}, interval); | ||
} | ||
} catch (e) { | ||
// Catch any extra errors with datafile updates | ||
zlib.gunzip(buffer, function (err, data) { | ||
let doneFileName = filename + "_done"; | ||
fs.writeFile(doneFileName, data, function () { | ||
dataFileUpdateService.fileReady(dataFile, doneFileName); | ||
}) | ||
}); | ||
}) | ||
} else { | ||
dataFileUpdateService.fileReady(dataFile, filename); | ||
} | ||
this.pipeline.log('error', e); | ||
} | ||
} | ||
checkNextUpdate(dataFile) { | ||
registerDataFile (dataFile) { | ||
dataFile.registered = true; | ||
try { | ||
let dataFileUpdateService = this; | ||
let interval = minToMs((Math.floor(Math.random() * dataFile.updateTimeMaximumRandomisation) + 1) + dataFile.pollingInterval); | ||
interval += dataFile.getNextUpdate().getMilliseconds(); | ||
// Run update on start if specified to do so | ||
if (dataFile.updateOnStart) { | ||
dataFileUpdateService.updateDataFile(dataFile); | ||
} else { | ||
setTimeout(function () { | ||
dataFileUpdateService.updateDataFile(dataFile); | ||
}, interval); | ||
} | ||
} catch (e) { | ||
// Catch any extra errors with datafile updates | ||
this.pipeline.log("error", e); | ||
} | ||
if (dataFile.updateOnStart) { | ||
this.updateDataFile(dataFile); | ||
} else { | ||
if (dataFile.autoUpdate) { | ||
this.checkNextUpdate(dataFile); | ||
} | ||
} | ||
registerDataFile(dataFile) { | ||
// check if fileSystemWatcher is enabled and listen for datafile changes | ||
dataFile.registered = true; | ||
if (dataFile.fileSystemWatcher) { | ||
const watch = function () { | ||
fs.watch(dataFile.path, { persistent: false }, function (event) { | ||
if (!dataFile.updating) { | ||
dataFile.refresh(); | ||
} | ||
}); | ||
}; | ||
if (dataFile.updateOnStart) { | ||
if (fs.existsSync(dataFile.path)) { | ||
watch(); | ||
} else { | ||
const checkWatcherCanStart = setInterval(function () { | ||
try { | ||
watch(); | ||
this.updateDataFile(dataFile); | ||
clearInterval(checkWatcherCanStart); | ||
} catch (e) { | ||
} else { | ||
if (dataFile.autoUpdate) { | ||
this.checkNextUpdate(dataFile); | ||
} | ||
} | ||
// check if fileSystemWatcher is enabled and listen for datafile changes | ||
if (dataFile.fileSystemWatcher) { | ||
let watch = function () { | ||
fs.watch(dataFile.path, { persistent: false }, function (event) { | ||
if (!dataFile.updating) { | ||
dataFile.refresh(); | ||
} | ||
}); | ||
}; | ||
if (fs.existsSync(dataFile.path)) { | ||
watch(); | ||
} else { | ||
let checkWatcherCanStart = setInterval(function () { | ||
try { | ||
watch(); | ||
clearInterval(checkWatcherCanStart); | ||
} catch (e) { | ||
} | ||
}, 500); | ||
} | ||
} | ||
} | ||
}, 500); | ||
} | ||
} | ||
} | ||
} | ||
module.exports = dataFileUpdateService; | ||
module.exports = DataFileUpdateService; |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,21 +17,19 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
class dataKeyedCache { | ||
/** | ||
class DataKeyedCache { | ||
/** | ||
* A simple cache getter that takes a cache key and returns an element if it is found in the cache | ||
* @param {Object} cachekey | ||
*/ | ||
get(cachekey) { | ||
get (cachekey) { | ||
} | ||
} | ||
/** | ||
/** | ||
* Add an element to the cache | ||
@@ -41,8 +39,7 @@ * @param {Object} cachekey | ||
*/ | ||
put(cachekey, value) { | ||
put (cachekey, value) { | ||
} | ||
} | ||
} | ||
module.exports = dataKeyedCache; | ||
module.exports = DataKeyedCache; |
168
engine.js
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,24 +17,23 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
let require51 = (requestedPackage) => { | ||
try { | ||
return require(__dirname + "/../" + requestedPackage) | ||
} catch (e) { | ||
return require(requestedPackage); | ||
} | ||
} | ||
const require51 = (requestedPackage) => { | ||
try { | ||
return require(__dirname + '/../' + requestedPackage); | ||
} catch (e) { | ||
return require(requestedPackage); | ||
} | ||
}; | ||
const flowElement = require51("fiftyone.pipeline.core").flowElement; | ||
const FlowElement = require51('fiftyone.pipeline.core').FlowElement; | ||
const dataFileUpdateService = require("./dataFileUpdateService"); | ||
const DataFileUpdateService = require('./dataFileUpdateService'); | ||
class engine extends flowElement { | ||
/** | ||
class Engine extends FlowElement { | ||
/** | ||
* Constructor for engine class, extends flowElement with extra options | ||
@@ -46,112 +45,81 @@ * | ||
*/ | ||
constructor( | ||
{ | ||
cache, restrictedProperties | ||
} = {} | ||
) { | ||
constructor ( | ||
{ | ||
cache, restrictedProperties | ||
} = {} | ||
) { | ||
super(...arguments); | ||
super(...arguments); | ||
if (cache) { | ||
this.cache = cache; | ||
} | ||
if (cache) { | ||
this.cache = cache; | ||
} | ||
if (restrictedProperties) { | ||
this.restrictedProperties = restrictedProperties; | ||
} | ||
if (restrictedProperties) { | ||
this.restrictedProperties = restrictedProperties; | ||
} | ||
} | ||
/** | ||
/** | ||
* Checks cache and returns cached result if found. | ||
* @param {flowData} flowData | ||
* @param {FlowData} flowData | ||
*/ | ||
inCache(flowData) { | ||
inCache (flowData) { | ||
let keys = this.evidenceKeyFilter.filterEvidence(flowData.evidence.getAll()); | ||
let keys = this.evidenceKeyFilter.filterEvidence(flowData.evidence.getAll()); | ||
keys = JSON.stringify(keys); | ||
keys = JSON.stringify(keys); | ||
const cached = this.cache.get(keys); | ||
let cached = this.cache.get(keys); | ||
if (cached) { | ||
flowData.setElementData(cached); | ||
if (cached) { | ||
flowData.setElementData(cached); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
/** | ||
/** | ||
* An engine's process function checks cache for an item (calling inCache) | ||
* If found it returns the cached object | ||
* If not found it runs the standard processInternal function and adds it to the cache (if a cache is present) | ||
* @param {flowData} flowData | ||
* @param {FlowData} flowData | ||
*/ | ||
process(flowData) { | ||
process (flowData) { | ||
const engine = this; | ||
let engine = this; | ||
if (this.cache) { | ||
if (this.inCache(flowData)) { | ||
return Promise.resolve(true); | ||
} | ||
} | ||
return Promise.resolve(this.processInternal(flowData)).then(function () { | ||
if (engine.cache) { | ||
let keys = engine.evidenceKeyFilter.filterEvidence(flowData.evidence.getAll()); | ||
keys = JSON.stringify(keys); | ||
engine.cache.put(keys, flowData.get(engine.dataKey)); | ||
} | ||
}); | ||
if (this.cache) { | ||
if (this.inCache(flowData)) { | ||
return Promise.resolve(true); | ||
} | ||
} | ||
refresh() { | ||
return Promise.resolve(this.processInternal(flowData)).then(function () { | ||
if (engine.cache) { | ||
let keys = engine.evidenceKeyFilter.filterEvidence(flowData.evidence.getAll()); | ||
return true; | ||
keys = JSON.stringify(keys); | ||
} | ||
engine.cache.put(keys, flowData.get(engine.dataKey)); | ||
} | ||
}); | ||
} | ||
registerDataFile(dataFile) { | ||
refresh () { | ||
return true; | ||
} | ||
this.registrationCallbacks.push(function (pipeline) { | ||
registerDataFile (dataFile) { | ||
this.registrationCallbacks.push(function (pipeline) { | ||
// Create datafile update service if not already created | ||
// Create datafile update service if not already created | ||
if (!pipeline.dataFileUpdateService) { | ||
pipeline.dataFileUpdateService = new DataFileUpdateService(pipeline); | ||
} | ||
if (!pipeline.dataFileUpdateService) { | ||
pipeline.dataFileUpdateService = new dataFileUpdateService(pipeline); | ||
} | ||
pipeline.dataFileUpdateService.registerDataFile(dataFile); | ||
}) | ||
} | ||
pipeline.dataFileUpdateService.registerDataFile(dataFile); | ||
}); | ||
} | ||
} | ||
module.exports = engine; | ||
module.exports = Engine; |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,6 +17,6 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
@@ -42,36 +42,28 @@ * ********************************************************************* */ | ||
const pipeline = require("fiftyone.pipeline.core"); | ||
const engine = require(".."); // change this to fiftyone.pipeline.engines in your code | ||
const pipeline = require('fiftyone.pipeline.core'); | ||
// Note that this example is designed to be run from within the | ||
// source repository. If this code has been copied to run standalone | ||
// then you'll need to replace the require below with the commented | ||
// out version below it. | ||
const engine = require('..'); | ||
// const engine = require("fiftyone.pipeline.engines"); | ||
const lruCache = new engine.lruCache(100); | ||
// This is a very simple cache that uses an in memory JavaScript object | ||
class myCustomCache extends engine.dataKeyedCache { | ||
class MyCustomCache extends engine.DataKeyedCache { | ||
constructor (size) { | ||
super(); | ||
constructor(size) { | ||
this.cache = {}; | ||
} | ||
super(); | ||
this.cache = {}; | ||
get (cachekey) { | ||
if (this.cache[cachekey]) { | ||
console.log('Fetched from cache'); | ||
return this.cache[cachekey]; | ||
} | ||
} | ||
get(cachekey) { | ||
if (this.cache[cachekey]) { | ||
console.log("Fetched from cache") | ||
return this.cache[cachekey]; | ||
} | ||
} | ||
put(cachekey, value) { | ||
this.cache[cachekey] = value; | ||
} | ||
put (cachekey, value) { | ||
this.cache[cachekey] = value; | ||
} | ||
} | ||
@@ -81,46 +73,38 @@ | ||
let cacheTest = new engine.engine({ | ||
dataKey: "cacheTest", | ||
cache: new myCustomCache(), | ||
evidenceKeyFilter: new pipeline.basicListEvidenceKeyFilter(["cookie.my-user-id"]), | ||
processInternal: function (flowData) { | ||
const cacheTest = new engine.Engine({ | ||
dataKey: 'cacheTest', | ||
cache: new MyCustomCache(), | ||
evidenceKeyFilter: new pipeline.BasicListEvidenceKeyFilter(['cookie.my-user-id']), | ||
processInternal: function (flowData) { | ||
const engine = this; | ||
let engine = this; | ||
return new Promise(function (resolve, reject) { | ||
const data = new pipeline.ElementDataDictionary({ flowElement: engine, contents: { hello: 'world' } }); | ||
return new Promise(function (resolve, reject) { | ||
flowData.setElementData(data); | ||
let data = new pipeline.elementDataDictionary({ flowElement: engine, contents: { "hello": "world" } }); | ||
flowData.setElementData(data); | ||
resolve(); | ||
}); | ||
}, | ||
properties: { | ||
hello: { | ||
type: "string" | ||
} | ||
resolve(); | ||
}); | ||
}, | ||
properties: { | ||
hello: { | ||
type: 'string' | ||
} | ||
} | ||
}); | ||
let cachePipeline = new pipeline.pipelineBuilder().add(cacheTest).build(); | ||
const cachePipeline = new pipeline.PipelineBuilder().add(cacheTest).build(); | ||
let flowData = cachePipeline.createFlowData(); | ||
const flowData = cachePipeline.createFlowData(); | ||
let processFlowDataWithUserId = function (userID) { | ||
const processFlowDataWithUserId = function (userID) { | ||
flowData.evidence.add('cookie.my-user-id', userID); | ||
flowData.evidence.add("cookie.my-user-id", userID); | ||
return flowData.process(); | ||
}; | ||
return flowData.process(); | ||
processFlowDataWithUserId('112'); | ||
} | ||
processFlowDataWithUserId("112"); | ||
setTimeout(function () { | ||
processFlowDataWithUserId("112"); | ||
processFlowDataWithUserId('112'); | ||
}, 100); |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,6 +17,6 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
@@ -28,6 +28,6 @@ * ********************************************************************* */ | ||
This example demonstrates the creation of a custom flow element which takes a | ||
birth date as evidence and uses it to check a lookup table for a starsign. | ||
This lookup table is stored in a JSON file which is registered using the | ||
datafile update service. In this case the file has a simple watcher which | ||
This example demonstrates the creation of a custom flow element which takes a | ||
birth date as evidence and uses it to check a lookup table for a starsign. | ||
This lookup table is stored in a JSON file which is registered using the | ||
datafile update service. In this case the file has a simple watcher which | ||
checks if the file has changed. | ||
@@ -38,140 +38,117 @@ | ||
// Require the filesystem module for datafile reading | ||
const fs = require("fs"); | ||
const fs = require('fs'); | ||
// First require the core Pipeline | ||
const FiftyOnePipelineCore = require("fiftyone.pipeline.core"); | ||
const FiftyOnePipelineCore = require('fiftyone.pipeline.core'); | ||
// Next require the engines extension that extends flowElements to support | ||
// functionality such as auto updating datafiles, caches and missing property services | ||
const FiftyOnePipelineEngines = require(__dirname +"/../"); // Change this to fiftyone.pipeline.engines in your code | ||
// Next require the engines extension that extends flowElements to support | ||
// functionality such as auto updating datafiles, caches and missing property services | ||
// Note that this example is designed to be run from within the | ||
// source repository. If this code has been copied to run standalone | ||
// then you'll need to replace the require below with the commented | ||
// out version below it. | ||
const FiftyOnePipelineEngines = require(__dirname + '/../'); | ||
// const FiftyOnePipelineEngines = require("fiftyone.pipeline.engines"); | ||
//! [class] | ||
//! [constructor] | ||
// Astrology flowElement | ||
class astrology extends FiftyOnePipelineEngines.engine { | ||
class Astrology extends FiftyOnePipelineEngines.Engine { | ||
constructor ({ datafile }) { | ||
super(...arguments); | ||
constructor({ datafile }) { | ||
// Create a datafile including a filesystem watcher that checks if | ||
// the datafile has changed. Test by changing the names of the | ||
// starsigns to see it update | ||
this.dataFile = new FiftyOnePipelineEngines.DataFile({ flowElement: this, path: datafile, autoUpdate: false, fileSystemWatcher: true }); | ||
super(...arguments); | ||
this.registerDataFile(this.dataFile); | ||
// Create a datafile including a filesystem watcher that checks if | ||
// the datafile has changed. Test by changing the names of the | ||
// starsigns to see it update | ||
this.dataFile = new FiftyOnePipelineEngines.dataFile({ flowElement: this, path: datafile, autoUpdate: false, fileSystemWatcher: true }); | ||
// datakey used to categorise data coming back from this flowElement in a pipeline | ||
this.dataKey = 'astrology'; | ||
this.registerDataFile(this.dataFile); | ||
// A filter (in this case a basic list) stating which evidence the | ||
// flowElement is interested in, in this case a query string | ||
this.evidenceKeyFilter = new FiftyOnePipelineCore.BasicListEvidenceKeyFilter(['query.dateOfBirth']); | ||
// datakey used to categorise data coming back from this flowElement in a pipeline | ||
this.dataKey = "astrology"; | ||
// Update the datafile | ||
this.refresh(); | ||
} | ||
//! [constructor] | ||
// A filter (in this case a basic list) stating which evidence the | ||
// flowElement is interested in, in this case a query string | ||
this.evidenceKeyFilter = new FiftyOnePipelineCore.basicListEvidenceKeyFilter(["query.dateOfBirth"]); | ||
// A function called when the datafile is updated / refreshed. In this | ||
// case it simply loads the JSON from the file into the engine's memory. | ||
// Update the datafile | ||
this.refresh(); | ||
//! [refresh] | ||
refresh () { | ||
const engine = this; | ||
} | ||
//! [constructor] | ||
fs.readFile(this.dataFile.path, 'utf8', function (err, data) { | ||
if (err) { | ||
return console.error(err); | ||
} | ||
// A function called when the datafile is updated / refreshed. In this | ||
// case it simply loads the JSON from the file into the engine's memory. | ||
data = JSON.parse(data); | ||
//! [refresh] | ||
refresh() { | ||
// Load the datafile into memory and parse it to make it more easily readable | ||
data = data.map(function (e) { | ||
const start = e[1].split('/'); | ||
const end = e[2].split('/'); | ||
let engine = this; | ||
return { starsign: e[0], startMonth: parseInt(start[1]), startDate: parseInt(start[0]), endMonth: parseInt(end[1]), endDate: parseInt(end[0]) }; | ||
}); | ||
fs.readFile(this.dataFile.path, "utf8", function (err, data) { | ||
engine.data = data; | ||
}); | ||
} | ||
//! [refresh] | ||
data = JSON.parse(data) | ||
// Internal processing function | ||
processInternal (flowData) { | ||
let dateOfBirth = flowData.evidence.get('query.dateOfBirth'); | ||
// Load the datafile into memory and parse it to make it more easily readable | ||
data = data.map(function (e) { | ||
// Collect data to save back into the flowData under this engine | ||
let start = e[1].split("/"); | ||
let end = e[2].split("/"); | ||
const result = {}; | ||
return { starsign: e[0], startMonth: parseInt(start[1]), startDate: parseInt(start[0]), endMonth: parseInt(end[1]), endDate: parseInt(end[0]) } | ||
// Lookup the date of birth using the provided and now parsed datafile | ||
}); | ||
if (dateOfBirth) { | ||
dateOfBirth = dateOfBirth.split('-'); | ||
engine.data = data; | ||
const month = parseInt(dateOfBirth[1]); | ||
const day = parseInt(dateOfBirth[2]); | ||
}); | ||
result.starSign = this.data.filter(function (date) { | ||
// Find starsigns in the correct month | ||
if (date.startMonth === month) { | ||
if (date.startDate > day) { | ||
return false; | ||
} else if (date.endMonth === month && date.endDate <= day) { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
} else if (date.endMonth === month) { | ||
if (date.endDate < day) { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
} else { | ||
return false; | ||
} | ||
})[0].starsign; | ||
}; | ||
} | ||
//! [refresh] | ||
// Save the data into an extension of the elementData class (in this case a simple dictionary subclass) | ||
const data = new FiftyOnePipelineCore.ElementDataDictionary({ | ||
flowElement: this, contents: result | ||
}); | ||
// Internal processing function | ||
processInternal(flowData) { | ||
let dateOfBirth = flowData.evidence.get("query.dateOfBirth"); | ||
// Collect data to save back into the flowData under this engine | ||
let result = {}; | ||
// Lookup the date of birth using the provided and now parsed datafile | ||
if (dateOfBirth) { | ||
dateOfBirth = dateOfBirth.split("-"); | ||
let month = parseInt(dateOfBirth[1]); | ||
let day = parseInt(dateOfBirth[2]); | ||
result.starSign = this.data.filter(function (date) { | ||
// Find starsigns in the correct month | ||
if (date.startMonth === month) { | ||
if (date.startDate > day) { | ||
return false; | ||
} else if (date.endMonth === month && date.endDate <= day) { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
} else if (date.endMonth === month) { | ||
if (date.endDate < day) { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
} else { | ||
return false; | ||
} | ||
})[0].starsign; | ||
}; | ||
// Save the data into an extension of the elementData class (in this case a simple dictionary subclass) | ||
let data = new FiftyOnePipelineCore.elementDataDictionary({ | ||
flowElement: this, contents: result | ||
}); | ||
// Set this data on the flowElement | ||
flowData.setElementData(data); | ||
} | ||
// Set this data on the flowElement | ||
flowData.setElementData(data); | ||
} | ||
} | ||
@@ -181,26 +158,24 @@ //! [class] | ||
//! [usage] | ||
let astrologyElement = new astrology({ datafile: (process.env.directory || __dirname) + "/astrology.json" }); | ||
const astrologyElement = new Astrology({ datafile: (process.env.directory || __dirname) + '/astrology.json' }); | ||
const http = require('http'); | ||
let pipeline = new FiftyOnePipelineCore.pipelineBuilder() | ||
.add(astrologyElement) | ||
.build(); | ||
const pipeline = new FiftyOnePipelineCore.PipelineBuilder() | ||
.add(astrologyElement) | ||
.build(); | ||
const server = http.createServer((req, res) => { | ||
const flowData = pipeline.createFlowData(); | ||
let flowData = pipeline.createFlowData(); | ||
// Add any information from the request (headers, cookies and additional client side provided information) | ||
flowData.evidence.addFromRequest(req); | ||
// Add any information from the request (headers, cookies and additional client side provided information) | ||
flowData.evidence.addFromRequest(req); | ||
flowData.process().then(function () { | ||
// Output the date of birth form with any results if they exist | ||
flowData.process().then(function () { | ||
const output = ` | ||
// Output the date of birth form with any results if they exist | ||
let output = ` | ||
<h1>Starsigns</h1> | ||
${flowData.astrology.starSign ? "<p>Your starsign is " + flowData.astrology.starSign + " </p>" : "<p>Add your date of birth to get your starsign</p>"} | ||
${flowData.astrology.starSign ? '<p>Your starsign is ' + flowData.astrology.starSign + ' </p>' : '<p>Add your date of birth to get your starsign</p>'} | ||
@@ -211,14 +186,11 @@ <form><label for='dateOfBirth'>Date of birth</label><input type='date' name='dateOfBirth' id='dateOfBirth'><input type='submit'></form> | ||
res.statusCode = 200; | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.end(output); | ||
}); | ||
res.statusCode = 200; | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.end(output); | ||
}); | ||
}); | ||
let portNum = process.env.PORT || 3000; | ||
console.info("To test this example, browse to http://localhost:" + portNum); | ||
const portNum = process.env.PORT || 3000; | ||
console.info('To test this example, browse to http://localhost:' + portNum); | ||
server.listen(portNum); | ||
//! [usage] |
29
index.js
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,6 +17,6 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
@@ -27,13 +27,12 @@ * ********************************************************************* */ | ||
aspectData: require("./aspectData"), | ||
aspectDataDictionary: require("./aspectDataDictionary"), | ||
aspectPropertyValue: require("./aspectPropertyValue"), | ||
dataFile: require("./dataFile"), | ||
dataKeyedCache: require("./dataKeyedCache"), | ||
engine: require("./engine"), | ||
lru: require("./lru"), | ||
lruCache: require("./lruCache"), | ||
missingPropertyService: require("./missingPropertyService"), | ||
tracker: require("./tracker") | ||
AspectData: require('./aspectData'), | ||
AspectDataDictionary: require('./aspectDataDictionary'), | ||
DataFile: require('./dataFile'), | ||
DataKeyedCache: require('./dataKeyedCache'), | ||
Engine: require('./engine'), | ||
Lru: require('./lru'), | ||
LruCache: require('./lruCache'), | ||
MissingPropertyService: require('./missingPropertyService'), | ||
Tracker: require('./tracker') | ||
} | ||
}; |
157
lru.js
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,6 +17,6 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
@@ -26,101 +26,98 @@ * ********************************************************************* */ | ||
class Node { | ||
constructor(key, value, next = null, prev = null) { | ||
this.key = key; | ||
this.value = value; | ||
this.next = next; | ||
this.prev = prev; | ||
} | ||
constructor (key, value, next = null, prev = null) { | ||
this.key = key; | ||
this.value = value; | ||
this.next = next; | ||
this.prev = prev; | ||
} | ||
} | ||
class LRU { | ||
constructor(limit = 1000) { | ||
this.size = 0; | ||
this.limit = limit; | ||
this.head = null; | ||
this.tail = null; | ||
this.cache = {}; | ||
} | ||
constructor (limit = 1000) { | ||
this.size = 0; | ||
this.limit = limit; | ||
this.head = null; | ||
this.tail = null; | ||
this.cache = {}; | ||
} | ||
write(key, value) { | ||
this.ensureLimit(); | ||
write (key, value) { | ||
this.ensureLimit(); | ||
if (!this.head) { | ||
this.head = this.tail = new Node(key, value); | ||
} else { | ||
const node = new Node(key, value, this.head); | ||
this.head.prev = node; | ||
this.head = node; | ||
} | ||
this.cache[key] = this.head; | ||
this.size++; | ||
if (!this.head) { | ||
this.head = this.tail = new Node(key, value); | ||
} else { | ||
const node = new Node(key, value, this.head); | ||
this.head.prev = node; | ||
this.head = node; | ||
} | ||
read(key) { | ||
if (this.cache[key]) { | ||
const value = this.cache[key].value; | ||
this.cache[key] = this.head; | ||
this.size++; | ||
} | ||
this.remove(key) | ||
this.write(key, value); | ||
read (key) { | ||
if (this.cache[key]) { | ||
const value = this.cache[key].value; | ||
return value; | ||
} else { | ||
this.remove(key); | ||
this.write(key, value); | ||
return null; | ||
} | ||
return value; | ||
} else { | ||
return null; | ||
} | ||
} | ||
ensureLimit() { | ||
if (this.size === this.limit) { | ||
this.remove(this.tail.key); | ||
} | ||
ensureLimit () { | ||
if (this.size === this.limit) { | ||
this.remove(this.tail.key); | ||
} | ||
} | ||
remove(key) { | ||
const node = this.cache[key]; | ||
remove (key) { | ||
const node = this.cache[key]; | ||
if (node.prev !== null) { | ||
node.prev.next = node.next; | ||
} else { | ||
this.head = node.next; | ||
} | ||
if (node.next !== null) { | ||
node.next.prev = node.prev; | ||
} else { | ||
this.tail = node.prev; | ||
} | ||
delete this.cache[key]; | ||
this.size--; | ||
if (node.prev !== null) { | ||
node.prev.next = node.next; | ||
} else { | ||
this.head = node.next; | ||
} | ||
clear() { | ||
this.head = null; | ||
this.tail = null; | ||
this.size = 0; | ||
this.cache = {}; | ||
if (node.next !== null) { | ||
node.next.prev = node.prev; | ||
} else { | ||
this.tail = node.prev; | ||
} | ||
forEach(fn) { | ||
let node = this.head; | ||
let counter = 0; | ||
while (node) { | ||
fn(node, counter); | ||
node = node.next; | ||
counter++; | ||
} | ||
delete this.cache[key]; | ||
this.size--; | ||
} | ||
clear () { | ||
this.head = null; | ||
this.tail = null; | ||
this.size = 0; | ||
this.cache = {}; | ||
} | ||
forEach (fn) { | ||
let node = this.head; | ||
let counter = 0; | ||
while (node) { | ||
fn(node, counter); | ||
node = node.next; | ||
counter++; | ||
} | ||
} | ||
*[Symbol.iterator]() { | ||
let node = this.head; | ||
while (node) { | ||
yield node; | ||
node = node.next; | ||
} | ||
* [Symbol.iterator] () { | ||
let node = this.head; | ||
while (node) { | ||
yield node; | ||
node = node.next; | ||
} | ||
} | ||
} | ||
module.exports = LRU; | ||
module.exports = LRU; |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,36 +17,28 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
const dataKeyedCache = require("./dataKeyedCache"); | ||
const LRU = require("./lru"); | ||
const DataKeyedCache = require('./dataKeyedCache'); | ||
const LRU = require('./lru'); | ||
class LRUcache extends dataKeyedCache { | ||
class LRUcache extends DataKeyedCache { | ||
constructor ({ size = 100 }) { | ||
super(...arguments); | ||
constructor({ size = 100 }) { | ||
this.cache = new LRU(size); | ||
} | ||
super(...arguments); | ||
get (cacheKey) { | ||
return this.cache.read(cacheKey); | ||
} | ||
this.cache = new LRU(size); | ||
} | ||
get(cacheKey) { | ||
return this.cache.read(cacheKey); | ||
} | ||
put(cacheKey, value) { | ||
this.cache.write(cacheKey, value); | ||
} | ||
put (cacheKey, value) { | ||
this.cache.write(cacheKey, value); | ||
} | ||
} | ||
module.exports = LRUcache; |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,12 +17,11 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
class missingPropertyService { | ||
/** | ||
class MissingPropertyService { | ||
/** | ||
* Simple base class for a missing property service that returns an error if the property is not available for some reason | ||
@@ -32,10 +31,7 @@ * @param {String} elementData key | ||
*/ | ||
check(key, flowElement) { | ||
throw "Property " + key + " not found in " + flowElement.dataKey; | ||
} | ||
check (key, flowElement) { | ||
throw 'Property ' + key + ' not found in ' + flowElement.dataKey; | ||
} | ||
} | ||
module.exports = missingPropertyService; | ||
module.exports = MissingPropertyService; |
{ | ||
"name": "fiftyone.pipeline.engines", | ||
"version": "4.1.0-beta.15", | ||
"version": "4.1.0", | ||
"description": "Shared base functionality for implementing engines for the 51Degrees Pipeline API", | ||
@@ -17,2 +17,8 @@ "main": "index.js", | ||
"devDependencies": { | ||
"eslint": "^6.8.0", | ||
"eslint-config-standard": "^14.1.1", | ||
"eslint-plugin-import": "^2.20.2", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint-plugin-promise": "^4.2.1", | ||
"eslint-plugin-standard": "^4.0.1", | ||
"jest": "^24.9.0" | ||
@@ -26,3 +32,10 @@ }, | ||
], | ||
"license": "EUPL-1.2" | ||
"license": "EUPL-1.2", | ||
"bugs": { | ||
"url": "https://github.com/51Degrees/pipeline-node/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/51Degrees/pipeline-node" | ||
} | ||
} |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,27 +17,26 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
let fs = require("fs"); | ||
let path = require("path"); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
let testExample = function ({ file, portNumber }) { | ||
const testExample = function ({ file, portNumber }) { | ||
if (portNumber) { | ||
process.env.PORT = portNumber; | ||
} | ||
if (portNumber) { | ||
process.env.PORT = portNumber; | ||
} | ||
// Change the working directory of the example to be the example itself | ||
// Change the working directory of the example to be the example itself | ||
process.env.directory = path.dirname(file); | ||
process.env.directory = path.dirname(file); | ||
let code = fs.readFileSync(file, 'utf8'); | ||
let code = fs.readFileSync(file, "utf8"); | ||
// Add in closer of any apps | ||
// Add in closer of any apps | ||
let serverClose = ` | ||
const serverClose = ` | ||
@@ -52,20 +51,13 @@ if(typeof server !== "undefined"){ | ||
code += serverClose; | ||
code += serverClose; | ||
jest.fn(eval(code)); | ||
jest.fn(eval(code)); | ||
}; | ||
} | ||
test('caching example', () => { | ||
testExample({ file: __dirname + "/../examples/caching.js" }); | ||
testExample({ file: __dirname + '/../examples/caching.js' }); | ||
}); | ||
test('on premise flow element example', () => { | ||
testExample({ file: __dirname + "/../examples/onPremiseFlowElement.js", portNumber: 3002 }); | ||
testExample({ file: __dirname + '/../examples/onPremiseFlowElement.js', portNumber: 3002 }); | ||
}); | ||
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,16 +17,16 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
let engine = require(__dirname + "/../engine"); | ||
let pipelineBuilder = require(__dirname + "/../../fiftyone.pipeline.core/pipelineBuilder"); | ||
let aspectDataDictionary = require(__dirname + "/../aspectDataDictionary"); | ||
let basicListEvidenceKeyFilter = require(__dirname + "/../../fiftyone.pipeline.core/basicListEvidenceKeyFilter"); | ||
let lruCache = require(__dirname + "/../lruCache"); | ||
const Engine = require(__dirname + '/../engine'); | ||
const PipelineBuilder = require(__dirname + '/../../fiftyone.pipeline.core/pipelineBuilder'); | ||
const AspectDataDictionary = require(__dirname + '/../aspectDataDictionary'); | ||
const BasicListEvidenceKeyFilter = require(__dirname + '/../../fiftyone.pipeline.core/basicListEvidenceKeyFilter'); | ||
const LruCache = require(__dirname + '/../lruCache'); | ||
let cache = new lruCache({ size: 1 }); | ||
const cache = new LruCache({ size: 1 }); | ||
@@ -37,102 +37,76 @@ // Flag to test cache | ||
let testEngine = new engine( | ||
{ | ||
dataKey: "testEngine", | ||
cache: cache, | ||
restrictedProperties: ["one", "noCache"], | ||
evidenceKeyFilter: new basicListEvidenceKeyFilter(["header.user-agent"]), | ||
properties: { | ||
"one": { | ||
"meta": { | ||
"type": "int" | ||
} | ||
}, | ||
"two": { | ||
"meta": { | ||
"type": "int" | ||
} | ||
} | ||
}, | ||
processInternal: function (flowData) { | ||
const testEngine = new Engine( | ||
{ | ||
dataKey: 'testEngine', | ||
cache: cache, | ||
restrictedProperties: ['one', 'noCache'], | ||
evidenceKeyFilter: new BasicListEvidenceKeyFilter(['header.user-agent']), | ||
properties: { | ||
one: { | ||
meta: { | ||
type: 'int' | ||
} | ||
}, | ||
two: { | ||
meta: { | ||
type: 'int' | ||
} | ||
} | ||
}, | ||
processInternal: function (flowData) { | ||
const contents = { one: 1, two: 2 }; | ||
let contents = { "one": 1, "two": 2 }; | ||
// Check if flowData has been processed before (for cache test) | ||
// Check if flowData has been processed before (for cache test) | ||
contents.noCache = hasRun; | ||
contents.noCache = hasRun; | ||
const data = new AspectDataDictionary({ flowElement: this, contents: contents }); | ||
let data = new aspectDataDictionary({ flowElement: this, contents: contents }); | ||
flowData.setElementData(data); | ||
flowData.setElementData(data); | ||
hasRun = true; | ||
} | ||
hasRun = true; | ||
} | ||
} | ||
); | ||
let flowData = new pipelineBuilder().add(testEngine).build().createFlowData(); | ||
const flowData = new PipelineBuilder().add(testEngine).build().createFlowData(); | ||
test('engine process', done => { | ||
flowData.process().then(function () { | ||
expect(flowData.get('testEngine').get('one')).toBe(1); | ||
flowData.process().then(function () { | ||
expect(flowData.get("testEngine").get("one")).toBe(1); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
test('restricted properties', done => { | ||
flowData.process().then(function () { | ||
try { | ||
flowData.get('testEngine').get('two'); | ||
} catch (e) { | ||
expect(e.indexOf('excluded') !== -1).toBe(true); | ||
} | ||
flowData.process().then(function () { | ||
try { | ||
flowData.get("testEngine").get("two"); | ||
} catch (e) { | ||
expect(e.indexOf("excluded") !== -1).toBe(true); | ||
} | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
test('missing property service', done => { | ||
flowData.process().then(function () { | ||
try { | ||
flowData.get('testEngine').get('three'); | ||
} catch (e) { | ||
expect(e.indexOf('not found') !== -1).toBe(true); | ||
} | ||
flowData.process().then(function () { | ||
try { | ||
flowData.get("testEngine").get("three"); | ||
} catch (e) { | ||
expect(e.indexOf("not found") !== -1).toBe(true); | ||
} | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
test('cache', done => { | ||
flowData.process().then(function () { | ||
expect(flowData.get('testEngine').get('noCache')).toBe(false); | ||
flowData.process().then(function () { | ||
expect(flowData.get("testEngine").get("noCache")).toBe(false); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); |
@@ -6,3 +6,3 @@ /* ********************************************************************* | ||
* | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* This Original Work is licensed under the European Union Public Licence (EUPL) | ||
* v.1.2 and is subject to its terms as set out below. | ||
@@ -17,45 +17,35 @@ * | ||
* clause in Article 5 of the EUPL shall not apply. | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* | ||
* If using the Work as, or as part of, a network application, by | ||
* including the attribution notice(s) required under Article 5 of the EUPL | ||
* in the end user terms of the application under an appropriate heading, | ||
* in the end user terms of the application under an appropriate heading, | ||
* such notice(s) shall fulfill the requirements of that article. | ||
* ********************************************************************* */ | ||
const dataKeyedCache = require("./dataKeyedCache"); | ||
const DataKeyedCache = require('./dataKeyedCache'); | ||
class tracker extends dataKeyedCache { | ||
/** | ||
* The track method calls the dataKeyedCache get method, if it receives a result it sends it onto a match function | ||
class Tracker extends DataKeyedCache { | ||
/** | ||
* The track method calls the dataKeyedCache get method, if it receives a result it sends it onto a match function | ||
* @param {Object} cachekey | ||
*/ | ||
track(key) { | ||
track (key) { | ||
const result = this.get(key); | ||
let result = this.get(key); | ||
if (!result) { | ||
return true; | ||
} else { | ||
return this.match(key, result); | ||
} | ||
if (!result) { | ||
return true; | ||
} else { | ||
return this.match(key, result); | ||
} | ||
} | ||
/** | ||
/** | ||
* If object is found in cache, the match function is called | ||
* @param {Object} result | ||
*/ | ||
match(result) { | ||
return true; | ||
} | ||
match (result) { | ||
return true; | ||
} | ||
} | ||
module.exports = tracker; | ||
module.exports = Tracker; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
1216
0
1
17
47655
7