From f10e97198e31b4c0433c753803fa263d790fdbfb Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Thu, 12 Aug 2021 11:55:57 -0300 Subject: [PATCH 01/24] First commit for the new Trails implementation --- package.json | 3 +- trail.js | 194 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 126 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index dc41578..fa2db2a 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "nearley": "^2.20.1", "request": "^2.88.0", "request-promise-native": "^1.0.8", - "shortid": "^2.2.14" + "shortid": "^2.2.14", + "linked-list": "git+ssh://git@github.ibm.com:keg-core/linked-list.git" }, "license": "MIT", "repository": { diff --git a/trail.js b/trail.js index 7e640bb..06626cb 100644 --- a/trail.js +++ b/trail.js @@ -5,6 +5,8 @@ 'use strict' +const {List, Item} = require('linked-list'); + const shortid = require('shortid'); const Types = require('./types'); const HKEntity = require('./hkentity'); @@ -12,14 +14,10 @@ const Connector = require('./connector'); const Link = require('./link'); const RoleTypes = require('./roletypes'); -const CONNECTOR_NAME = 'occurs'; - -const vConnector = new Connector (CONNECTOR_NAME, 'f'); -vConnector.addRole ('sub', RoleTypes.SUBJECT); -vConnector.addRole ('obj', RoleTypes.OBJECT); -function Trail (id, parent) +function Trail (id, actions, parent) { + if(arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) { let trail = arguments[0]; @@ -29,8 +27,11 @@ function Trail (id, parent) this.properties = trail.properties || {}; this.metaproperties = trail.metaproperties || {}; this.interfaces = trail.interfaces || {}; - this.children = trail.children || []; - _loadSteps.call (this); + this.actions = trail.actions || []; + + if (this.actions && Object.keys(this.actions).length > 0) { + loadActions.call (this); + } } else { @@ -39,57 +40,43 @@ function Trail (id, parent) this.interfaces = {}; this.properties = {}; this.metaproperties = {}; - this.children = []; - this.steps = []; + this.actions = actions || []; } this.type = Types.TRAIL; } Trail.prototype = Object.create (HKEntity.prototype); -Trail.prototype.constructor = Trail; -Trail.prototype.addStep = function (key, properties) -{ - let ts = properties.begin || new Date().toISOString(); - properties.begin = ts; +Object.assign(Trail.prototype, List.prototype); // Multiple inheritance with assign - if (this.steps.length > 0) - { - let lastStep = this.steps[this.steps.length - 1].key; - if (! this.interfaces[lastStep].properties.end ) - { - this.interfaces[lastStep].properties.end = ts; - } - } +Trail.prototype.constructor = Trail; - this.steps.push ({key: key, begin: ts}); - this.addInterface (key, 'temporal', properties); -} -Trail.prototype.addInterface = function (key, type, properties) +// Update a given action in trail +Trail.prototype.updateAction = function (oldAction, newAction) { - this.interfaces[key] = {type: type, properties: properties} + oldAction.prepend(newAction); + oldAction.detach(); } -Trail.prototype.createLinksFromSteps = function () +// Remove an action from trail using its reference +Trail.prototype.removeAction = function (action) { - let vEntities = [vConnector]; - - for (let key in this.interfaces) - { - let interProp = this.interfaces[key].properties; - if (!interProp) continue; - - if (interProp.obj) - { - let l = new Link (shortid(), vConnector.id, this.parent); - l.addBind ('sub', interProp.obj, interProp.objInterface); - l.addBind ('obj', this.id); - vEntities.push (l); - } - } - return vEntities; + var action; + if(typeof(action) === 'string') { + this.action = this.search(action) + + //action not found + if(!this.action){ + return + } + } + else { + this.action = action; + } + + this.action.detach(); } Trail.prototype.serialize = function () @@ -100,40 +87,40 @@ Trail.prototype.serialize = function () properties: this.properties, metaproperties: this.metaproperties, interfaces: this.interfaces, + actions: this.actions, type: this.type }; } -function _loadSteps () +function loadActions(actions = null) { - let steps = []; - for (let key in this.interfaces) + if(actions) { - let begin = new Date (Date.parse(this.interfaces[key].properties.begin)); - let end = new Date (Date.parse(this.interfaces[key].properties.end)); - - this.interfaces[key].properties.begin = begin; - this.interfaces[key].properties.end = end; - - steps.push ( { key: key, begin: begin } ); + this.actions = actions; + return; } - steps.sort ( - (a,b) => + let actionArray = [] + for (let i in this.actions) + { + // create Action objects from parsed data + if (this.actions[i].hasOwnProperty("from") && + this.actions[i].hasOwnProperty("to") && + this.actions[i].hasOwnProperty("agent") && + this.actions[i].hasOwnProperty("eventType")) { - if (a.begin < b.begin ) - { - return -1; - } - if (a.begin > b.begin ) - { - return 1; - } + let from = new TrailNode(this.actions[i].from.split('#')[0], this.actions[i].from.split('#').length > 1 ? this.actions[i].from.split('#')[1] : "lambda") + let to = new TrailNode(this.actions[i].to.split('#')[0], this.actions[i].to.split('#').length > 1 ? this.actions[i].to.split('#')[1] : "lambda") + let event = { "id": i, "type": this.actions[i].eventType, "properties": this.actions[i].eventProperties, "timestamp": new Date(parseInt(this.actions[i].hasTimestamp))} + let agent = this.actions[i].agent - return 0; - }); - - this.steps = steps; + actionArray.push(new Action(from, to, event, agent)); + } + } + if (actionArray.length>0) + { + this.actions = actionArray; + } } function isValid(entity) @@ -151,7 +138,74 @@ function isValid(entity) return isValid; } +// TrailNode is basically an envelope for a hknode and a target anchor +class TrailNode { + constructor(nodeId, targetAnchor) { + this.nodeId = nodeId; + this.targetAnchor = targetAnchor; + } + + toString() { + return this.nodeId + "#" + this.targetAnchor; + } + + toJSON() { + return this.nodeId + "#" + this.targetAnchor; + } +} + +// actions hold references to source and destination trail +// nodes (along with their respective target anchors), +// the related event and an agent (user, system or content). +class Action extends Item { + constructor(from, to, event, agent) + { + super() + this.from = from; + this.to = to; + this.agent = agent; + this.event = event; + + if(!this.event) + { + return + } + + // create timestamp if needed + if(!this.event['timestamp']) + { + this.event['timestamp'] = new Date().getTime(); + } + + // get event id or create a new one + if(!this.event['id'] || this.event['id'] == '') + { + this.event['id'] = this.event.type + '_' + this.event.timestamp; + this.id = this.event['id']; + } + else + { + this.id = this.event['id']; + } + } + + getTime() + { + return new Date(this.event['timestamp']).getTime(); + } + + toString() + { + return "[" + new Date(this.event['timestamp']).toISOString() + "] " + this.from + " -- " + this.event['eventId'] + " (by " + this.agent + ") --> " + this.to; + } +} + Trail.type = Types.TRAIL; Trail.isValid = isValid; -module.exports = Trail; + +module.exports = { + Action : Action, + List : Trail +} + From b01ee370d46ebdca20341615e557917acb66976a Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Thu, 12 Aug 2021 14:17:49 -0300 Subject: [PATCH 02/24] Avoid exposing List class from Trail module --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6d084e4..9f1566b 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ exports.Context = require("./context"); exports.Connector = require("./connector"); exports.Reference = require("./reference"); exports.Link = require("./link"); -exports.Trail = require("./trail"); +exports.Trail = require("./trail").List; exports.HKEntity = require("./hkentity"); const Types = require("./types"); From bb802643ab4475011f44f4547dd273f38f40072e Mon Sep 17 00:00:00 2001 From: Raphael Melo Date: Wed, 18 Aug 2021 13:53:09 -0300 Subject: [PATCH 03/24] only exporting trail in trail.js --- trail.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/trail.js b/trail.js index 06626cb..f3b1c9d 100644 --- a/trail.js +++ b/trail.js @@ -204,8 +204,5 @@ Trail.type = Types.TRAIL; Trail.isValid = isValid; -module.exports = { - Action : Action, - List : Trail -} +module.exports = Trail; From f6df1d857399a8a84bc25c7e8307a1037b1ef7ac Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Wed, 18 Aug 2021 15:52:42 -0300 Subject: [PATCH 04/24] Removing List from require --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 9f1566b..6d084e4 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ exports.Context = require("./context"); exports.Connector = require("./connector"); exports.Reference = require("./reference"); exports.Link = require("./link"); -exports.Trail = require("./trail").List; +exports.Trail = require("./trail"); exports.HKEntity = require("./hkentity"); const Types = require("./types"); From c7171285dfcb3d4580427fa222234f78465d832b Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Tue, 24 Aug 2021 15:30:55 -0300 Subject: [PATCH 05/24] Adding in-memory functions Changing TrailNodes in Actions to objects instead of strings --- trail.js | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 190 insertions(+), 19 deletions(-) diff --git a/trail.js b/trail.js index f3b1c9d..dafc76b 100644 --- a/trail.js +++ b/trail.js @@ -79,17 +79,171 @@ Trail.prototype.removeAction = function (action) this.action.detach(); } -Trail.prototype.serialize = function () -{ - return { - id: this.id, - parent: this.parent, - properties: this.properties, - metaproperties: this.metaproperties, - interfaces: this.interfaces, - actions: this.actions, - type: this.type - }; +// aliases `in` and `out` represent the entrypoint and +// output TrailNodes for this trail, which conceptually is +// done through the use of Port elements in Hyperknowledge +Trail.prototype.in = function(from) { + if (from) + { + this.head.from = from; + } + + return this.head.from; +} +Trail.prototype.out = function(to) { + if (to) + { + this.tail.to = to; + } + + return this.tail.to; +} + +Trail.prototype.join = function(delimiter) { + return this.toArray().join(delimiter) +} + +// Update a given action in the trail +Trail.prototype.update = function(action, newAction){ + action.prepend(newAction) + action.detach() +} + +// Remove an action from the trail using its reference +Trail.prototype.remove = function(action) { + var action + if(typeof(action) === 'string') { + this.action = this.search(action) + + //action not found + if(!this.action){ + return + } + } + else { + this.action = action + } + + this.action.detach() +} + +// Get `num` actions (or the available ones) before `position` +Trail.prototype.getPrev = function(position, num=1){ + //check if it is a valid position + if (position >= this.size || position <= 1 || this.size <= 1){ + return; + } + + // get actions + var action = this.toArray()[position] + var resultSet = [] + while(action && action.prev){ + if (resultSet.length >= num) { + if(resultSet.length == 1 && num == 1){ + return resultSet[0] + } + return resultSet + } + else { + resultSet.push(action.prev) + } + + action = action.prev + } + + return resultSet +} + +// Get `num` actions (or a n= this.size || position <= 1 || this.size <= 1){ + return; + } + + // get actions + var action = this.toArray()[position] + var resultSet = [] + while(action && action.next){ + if (resultSet.length >= num) { + if(resultSet.length == 1 && num == 1){ + return resultSet[0] + } + } + else { + resultSet.push(action.next) + } + + action = action.next + } + + return resultSet +} + +// Get the position of an action in the list, +// it might be useful for pagination. Returns +// -1 in case action is not found in the list +Trail.prototype.getPositionOf = function(action){ + //search action by eventId + var array = this.toArray() + for (var i = 0; i < this.size; i++) { + if(array[i].event["eventId"] == action.event["eventId"]){ + return i + } + } + return -1 +} + +// Get an action at specified position +// a position = 0 is equivalent to trail.head +Trail.prototype.getActionAt = function(position) { + //check if it is a valid position + if (position < 0 || position >= this.size ){ + return; + } + + //return action by position in array + return this.toArray()[position] +} + +// Search for action(s) either by an event identifier or by filters +Trail.prototype.search = function(eventId = null, filters){ + if (!filters && eventId instanceof String) { + filters = {'from': null, 'fromAnchor': 'lambda', 'to': null, 'toAnchor': 'lambda'} + } + else if (!filters && eventId instanceof Object){ + filters = eventId + eventId = null + } + + // nothing to be found + if (!eventId && !filters['from'] && !filters['to']){ + return + } + + // search actions by eventId + var array = this.toArray() + if (eventId){ + for (var i = 0; i < this.size; i++) { + if(array[i].event["eventId"] == eventId){ + return array[i] + } + } + return + } + + // search actions by filters + var resultSet = [] + for (var i = 0; i < this.size; i++) { + if((filters['from'] && filters['to']) && array[i].from == filters['from'] && array[i].to == filters['to']){ + resultSet.push(array[i]) + } + else if(array[i].from == filters['from'] || array[i].to == filters['to']){ + resultSet.push(array[i]) + } + } + return resultSet } function loadActions(actions = null) @@ -109,9 +263,9 @@ function loadActions(actions = null) this.actions[i].hasOwnProperty("agent") && this.actions[i].hasOwnProperty("eventType")) { - let from = new TrailNode(this.actions[i].from.split('#')[0], this.actions[i].from.split('#').length > 1 ? this.actions[i].from.split('#')[1] : "lambda") - let to = new TrailNode(this.actions[i].to.split('#')[0], this.actions[i].to.split('#').length > 1 ? this.actions[i].to.split('#')[1] : "lambda") - let event = { "id": i, "type": this.actions[i].eventType, "properties": this.actions[i].eventProperties, "timestamp": new Date(parseInt(this.actions[i].hasTimestamp))} + let from = new TrailNode(JSON.parse(this.actions[i].from).nodeId, JSON.parse(this.actions[i].from).nodeType, JSON.parse(this.actions[i].from).targetAnchor); + let to = new TrailNode(JSON.parse(this.actions[i].to).nodeId, JSON.parse(this.actions[i].to).nodeType, JSON.parse(this.actions[i].to).targetAnchor); + let event = { "id": i, "type": this.actions[i].eventType, "properties": JSON.parse(this.actions[i].eventProperties), "timestamp": new Date(this.actions[i].hasTimestamp)} let agent = this.actions[i].agent actionArray.push(new Action(from, to, event, agent)); @@ -138,10 +292,26 @@ function isValid(entity) return isValid; } + +Trail.prototype.serialize = function () +{ + return { + id: this.id, + parent: this.parent, + properties: this.properties, + metaproperties: this.metaproperties, + interfaces: this.interfaces, + actions: this.actions, + type: this.type + }; +} + + // TrailNode is basically an envelope for a hknode and a target anchor class TrailNode { - constructor(nodeId, targetAnchor) { + constructor(nodeId, nodeType, targetAnchor) { this.nodeId = nodeId; + this.nodeType = nodeType; this.targetAnchor = targetAnchor; } @@ -149,9 +319,9 @@ class TrailNode { return this.nodeId + "#" + this.targetAnchor; } - toJSON() { - return this.nodeId + "#" + this.targetAnchor; - } + // toJSON() { + // return this.nodeId + "#" + this.targetAnchor; + // } } // actions hold references to source and destination trail @@ -202,7 +372,8 @@ class Action extends Item { Trail.type = Types.TRAIL; Trail.isValid = isValid; - +// Trail.Action = Action; +// Trail.TrailNode = TrailNode; module.exports = Trail; From 906b5adf5b854d3192b85c15a9864fd8ef7d9f67 Mon Sep 17 00:00:00 2001 From: Raphael Melo Thiago Date: Wed, 25 Aug 2021 16:54:30 -0300 Subject: [PATCH 06/24] Adding fetch trail to hkdatasource --- datasource/hkdatasource.js | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/datasource/hkdatasource.js b/datasource/hkdatasource.js index 00ba0fc..790fc02 100644 --- a/datasource/hkdatasource.js +++ b/datasource/hkdatasource.js @@ -463,6 +463,53 @@ HKDatasource.prototype.fetchContext = function(context, callback = () => {}) }); } +/** + * @callback GetTrailCallback + * @param {string} err An error object that indicate if the operation was succesful or not + * @param {object} trail + */ +/** + * Fetch trail + * + * @param {string} trailId The trail id to retrieve their nested entities. + * @param {GetTrailCallback} callback Callback with the entities + */ + + HKDatasource.prototype.fetchTrail = function(trailId, callback = () => {}) + { + let url = this.url + "repository/" + this.graphName + "/trail/" + encodeURIComponent(trailId); + + request.get(url, this.options, (err, res) => + { + // console.log(res.body); + if(!err) + { + if(requestCompletedWithSuccess (res.statusCode)) + { + try + { + let entities = convertEntities(res.body); + callback(null, entities[trailId]); + } + catch(exp) + { + console.log(exp); + callback(exp); + } + } + else + { + callback(`Server responded with ${res.statusCode}. ${res.body}`); + } + + } + else + { + callback(err); + } + }); + } + /** * Filter entities using CSS pattern `(TODO: document it better)` * From 6f1709c04a915777f8fff6a7d3ac1a4a456f69a2 Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Wed, 1 Sep 2021 15:59:55 -0300 Subject: [PATCH 07/24] Sorting actions based on timestamps --- trail.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/trail.js b/trail.js index dafc76b..6845759 100644 --- a/trail.js +++ b/trail.js @@ -82,7 +82,7 @@ Trail.prototype.removeAction = function (action) // aliases `in` and `out` represent the entrypoint and // output TrailNodes for this trail, which conceptually is // done through the use of Port elements in Hyperknowledge -Trail.prototype.in = function(from) { +Trail.prototype.in = function(from = null) { if (from) { this.head.from = from; @@ -90,7 +90,7 @@ Trail.prototype.in = function(from) { return this.head.from; } -Trail.prototype.out = function(to) { +Trail.prototype.out = function(to = null) { if (to) { this.tail.to = to; @@ -248,13 +248,17 @@ Trail.prototype.search = function(eventId = null, filters){ function loadActions(actions = null) { - if(actions) + + // use array of actions if passed + if(actions && Array.isArray(actions)) { - this.actions = actions; + this.actions = sort(actions); + return; } let actionArray = [] + let actionIds = [] for (let i in this.actions) { // create Action objects from parsed data @@ -270,11 +274,27 @@ function loadActions(actions = null) actionArray.push(new Action(from, to, event, agent)); } + else if(this.actions[i].hasOwnProperty("hasTimestamp")) + { + // all we got is event's id and timestamp + actionIds.push(new Action(null, null, {"id": i, "timestamp": new Date(this.actions[i].hasTimestamp)}, null)); + } } + if (actionArray.length>0) { - this.actions = actionArray; + this.actions = sort(actionArray); + } + else if (actionIds.length>0) + { + actionIds = sort(actionIds); + + for (let i in actionIds) + { + this.actions.push(actionIds[i].id); + } } + } function isValid(entity) @@ -292,6 +312,25 @@ function isValid(entity) return isValid; } +function sort(actions = null) +{ + if (actions && Array.isArray(actions) && actions[0] instanceof Object) + { + // sort and return object array based on timestamp + return actions.sort(function(action1, action2) + { + return new Date(action1.event['timestamp']) - new Date(action2.event['timestamp']); + }); + } + else if (actions && Array.isArray(actions) && this.actions[0] instanceof Action) + { + // sort Action array based on timestamp + return actions.sort(function(action1, action2) + { + return action1.event['timestamp'] - action2.event['timestamp']; + }); + } +} Trail.prototype.serialize = function () { @@ -372,6 +411,8 @@ class Action extends Item { Trail.type = Types.TRAIL; Trail.isValid = isValid; +Trail.sort = sort; + // Trail.Action = Action; // Trail.TrailNode = TrailNode; From 748073ce0118a24211a50cd44a1ddee31f418167 Mon Sep 17 00:00:00 2001 From: GabrielaPC Date: Wed, 8 Sep 2021 13:25:54 -0300 Subject: [PATCH 08/24] Trail Internals now appear on the Inspector --- hkgraph.js | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/hkgraph.js b/hkgraph.js index 119dec0..463b4f1 100644 --- a/hkgraph.js +++ b/hkgraph.js @@ -28,10 +28,10 @@ function HKGraph() this.linkMap = {}; this.refMap = {}; this.orphans = {}; - this.contextMap = {}; + this.compositeNodeMap = {}; this.relationless = {}; - this.contextMap[null] = {}; + this.compositeNodeMap[null] = {}; this.generateId = generateId; } @@ -72,7 +72,7 @@ HKGraph.prototype.setEntity = function(entity) oldEntity.className = entity.className; } - if(entity.type === Types.NODE || entity.type === Types.REFERENCE || entity.type === Types.CONTEXT) + if(entity.type === Types.NODE || entity.type === Types.TRAIL || entity.type === Types.REFERENCE || entity.type === Types.CONTEXT) { oldEntity.interfaces = entity.interfaces; @@ -86,7 +86,7 @@ HKGraph.prototype.setEntity = function(entity) let oldParent = this.getEntity(oldEntity.parent); if(oldParent) { - delete this.contextMap[oldEntity.parent][oldEntity.id]; + delete this.compositeNodeMap[oldEntity.parent][oldEntity.id]; } else if (oldEntity.parent) { @@ -100,7 +100,7 @@ HKGraph.prototype.setEntity = function(entity) let parent = this.getEntity(entity.parent); if(parent || entity.parent === null) { - this.contextMap[entity.parent][entity.id] = entity; + this.compositeNodeMap[entity.parent][entity.id] = entity; } else if (entity.parent) { @@ -164,11 +164,11 @@ HKGraph.prototype.addEntity = function(entity) { newEntity = new Context(entity); this.contexts[entity.id] = newEntity; - this.contextMap[entity.id] = {}; + this.compositeNodeMap[entity.id] = {}; if(this.orphans.hasOwnProperty(entity.id)) { - this.contextMap[entity.id] = this.orphans[entity.id]; + this.compositeNodeMap[entity.id] = this.orphans[entity.id]; delete this.orphans[entity.id]; } } @@ -180,6 +180,13 @@ HKGraph.prototype.addEntity = function(entity) { newEntity = new Trail(entity); this.trails[entity.id] = newEntity; + this.compositeNodeMap[entity.id] = {}; + + if(this.orphans.hasOwnProperty(entity.id)) + { + this.compositeNodeMap[entity.id] = this.orphans[entity.id]; + delete this.orphans[entity.id]; + } } break; } @@ -247,9 +254,9 @@ HKGraph.prototype.addEntity = function(entity) // Set parent if (entity.type !== Types.CONNECTOR) { - if (this.contextMap.hasOwnProperty(newEntity.parent)) + if (this.compositeNodeMap.hasOwnProperty(newEntity.parent)) { - this.contextMap[newEntity.parent][newEntity.id] = newEntity; + this.compositeNodeMap[newEntity.parent][newEntity.id] = newEntity; } else { @@ -291,7 +298,7 @@ HKGraph.prototype.removeEntity = function(id) case Context.type: { delete this.contexts[id]; - delete this.contextMap[entity.id]; + delete this.compositeNodeMap[entity.id]; break; } case Reference.type: @@ -340,6 +347,7 @@ HKGraph.prototype.removeEntity = function(id) { /* delete children trails? */ delete this.trails[id]; + delete this.compositeNodeMap[entity.id]; break; } } @@ -349,9 +357,9 @@ HKGraph.prototype.removeEntity = function(id) delete this.orphans[entity.parent][id]; } - if(this.contextMap.hasOwnProperty(entity.parent)) + if(this.compositeNodeMap.hasOwnProperty(entity.parent)) { - delete this.contextMap[entity.parent][entity.id]; + delete this.compositeNodeMap[entity.parent][entity.id]; } if(this.bindsMap.hasOwnProperty(entity.id)) @@ -435,9 +443,9 @@ HKGraph.prototype.getReference = function(id, parent) HKGraph.prototype.getChildren = function(contextId) { - if(this.contextMap.hasOwnProperty(contextId)) + if(this.compositeNodeMap.hasOwnProperty(contextId)) { - return this.contextMap[contextId]; + return this.compositeNodeMap[contextId]; } else { From 1e629566405f0d6898175665ab3b44314df249d8 Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Tue, 21 Sep 2021 17:15:12 -0300 Subject: [PATCH 09/24] Improving use of linked-list lib for in-memory operations --- trail.js | 237 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 153 insertions(+), 84 deletions(-) diff --git a/trail.js b/trail.js index 6845759..aec2536 100644 --- a/trail.js +++ b/trail.js @@ -5,52 +5,53 @@ 'use strict' -const {List, Item} = require('linked-list'); +const {List, Item} = require('linked-list'); +// const List = Linked.List; +// const Item = Linked.Item; -const shortid = require('shortid'); +// const shortid = require('shortid'); const Types = require('./types'); const HKEntity = require('./hkentity'); -const Connector = require('./connector'); -const Link = require('./link'); -const RoleTypes = require('./roletypes'); +// const Connector = require('./connector'); +// const Link = require('./link'); +// const RoleTypes = require('./roletypes'); function Trail (id, actions, parent) -{ - - if(arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) { - let trail = arguments[0]; - this.id = trail.id || null; - this.parent = trail.parent || null; - this.properties = trail.properties || {}; - this.metaproperties = trail.metaproperties || {}; - this.interfaces = trail.interfaces || {}; - this.actions = trail.actions || []; + if(arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) + { + let trail = arguments[0]; - if (this.actions && Object.keys(this.actions).length > 0) { - loadActions.call (this); + this.id = trail.id || null; + this.parent = trail.parent || null; + this.properties = trail.properties || {}; + this.metaproperties = trail.metaproperties || {}; + this.interfaces = trail.interfaces || {}; + this.actions = trail.actions || new List(); + + if (this.actions && Object.keys(this.actions).length > 0) { + loadActions.call(this); + } } - } - else - { - this.id = id || null; - this.parent = parent || null; - this.interfaces = {}; - this.properties = {}; - this.metaproperties = {}; - this.actions = actions || []; - } + else + { + this.id = id || null; + this.parent = parent || null; + this.interfaces = {}; + this.properties = {}; + this.metaproperties = {}; + this.actions = actions || new List(); - this.type = Types.TRAIL; + } + + this.type = Types.TRAIL; } Trail.prototype = Object.create (HKEntity.prototype); - -Object.assign(Trail.prototype, List.prototype); // Multiple inheritance with assign - Trail.prototype.constructor = Trail; +// Trail.prototype = Object.assign(Trail.prototype, List.prototype); // Multiple inheritance with assign // Update a given action in trail @@ -60,6 +61,24 @@ Trail.prototype.updateAction = function (oldAction, newAction) oldAction.detach(); } +// Add an action to a trail respecting timestamp ordering +Trail.prototype.addAction = function (action) +{ + var current = this.actions.head; + + while (current) + { + if (new Date(current.event['timestamp']) - new Date(action.event['timestamp'])) + { + current.prepend(action); + return; + } + current = current.next; + } + + this.actions.append(action); +} + // Remove an action from trail using its reference Trail.prototype.removeAction = function (action) { @@ -79,38 +98,54 @@ Trail.prototype.removeAction = function (action) this.action.detach(); } -// aliases `in` and `out` represent the entrypoint and -// output TrailNodes for this trail, which conceptually is +// aliases `in` and `out` represent the input and +// output TrailNodes for a trail, which conceptually is // done through the use of Port elements in Hyperknowledge Trail.prototype.in = function(from = null) { if (from) { - this.head.from = from; + this.actions.head.from = from; } - return this.head.from; + return this.actions.head.from; } + Trail.prototype.out = function(to = null) { if (to) { - this.tail.to = to; + this.actions.tail.to = to; } - return this.tail.to; + return this.actions.tail.to; +} + +Trail.prototype.size = function() +{ + if (this.actions.constructor === List) + { + return this.actions.size; + } + else if (Array.isArray(this.actions)) + { + return this.actions.length; + } } -Trail.prototype.join = function(delimiter) { - return this.toArray().join(delimiter) +Trail.prototype.join = function(delimiter) +{ + return this.actions.toArray().join(delimiter) } // Update a given action in the trail -Trail.prototype.update = function(action, newAction){ +Trail.prototype.update = function(action, newAction) +{ action.prepend(newAction) action.detach() } // Remove an action from the trail using its reference -Trail.prototype.remove = function(action) { +Trail.prototype.remove = function(action) +{ var action if(typeof(action) === 'string') { this.action = this.search(action) @@ -130,12 +165,12 @@ Trail.prototype.remove = function(action) { // Get `num` actions (or the available ones) before `position` Trail.prototype.getPrev = function(position, num=1){ //check if it is a valid position - if (position >= this.size || position <= 1 || this.size <= 1){ + if (position >= this.actions.size || position <= 1 || this.actions.size <= 1){ return; } // get actions - var action = this.toArray()[position] + var action = this.actions.toArray()[position] var resultSet = [] while(action && action.prev){ if (resultSet.length >= num) { @@ -158,12 +193,12 @@ Trail.prototype.getPrev = function(position, num=1){ // if a number is not given, 1 is assumed Trail.prototype.getNext = function(position, num=1){ // check if it is a valid position - if (position >= this.size || position <= 1 || this.size <= 1){ + if (position >= this.actions.size || position <= 1 || this.actions.size <= 1){ return; } // get actions - var action = this.toArray()[position] + var action = this.actions.toArray()[position] var resultSet = [] while(action && action.next){ if (resultSet.length >= num) { @@ -186,25 +221,25 @@ Trail.prototype.getNext = function(position, num=1){ // -1 in case action is not found in the list Trail.prototype.getPositionOf = function(action){ //search action by eventId - var array = this.toArray() - for (var i = 0; i < this.size; i++) { + var array = this.actions.toArray(); + for (var i = 0; i < this.actions.size; i++) { if(array[i].event["eventId"] == action.event["eventId"]){ - return i + return i; } } - return -1 + return -1; } // Get an action at specified position // a position = 0 is equivalent to trail.head Trail.prototype.getActionAt = function(position) { //check if it is a valid position - if (position < 0 || position >= this.size ){ + if (position < 0 || position >= this.actions.size ){ return; } //return action by position in array - return this.toArray()[position] + return this.actions.toArray()[position] } // Search for action(s) either by an event identifier or by filters @@ -223,19 +258,19 @@ Trail.prototype.search = function(eventId = null, filters){ } // search actions by eventId - var array = this.toArray() + var array = this.actions.toArray(); if (eventId){ - for (var i = 0; i < this.size; i++) { + for (var i = 0; i < this.actions.size; i++) { if(array[i].event["eventId"] == eventId){ - return array[i] + return array[i]; } } - return + return; } // search actions by filters var resultSet = [] - for (var i = 0; i < this.size; i++) { + for (var i = 0; i < this.actions.size; i++) { if((filters['from'] && filters['to']) && array[i].from == filters['from'] && array[i].to == filters['to']){ resultSet.push(array[i]) } @@ -252,8 +287,7 @@ function loadActions(actions = null) // use array of actions if passed if(actions && Array.isArray(actions)) { - this.actions = sort(actions); - + this.actions = new List(...sort(actions)); return; } @@ -261,40 +295,47 @@ function loadActions(actions = null) let actionIds = [] for (let i in this.actions) { - // create Action objects from parsed data - if (this.actions[i].hasOwnProperty("from") && + // create Action object from hkb parsed data + if (this.actions[i] && this.actions[i].hasOwnProperty("from") && this.actions[i].hasOwnProperty("to") && this.actions[i].hasOwnProperty("agent") && this.actions[i].hasOwnProperty("eventType")) { let from = new TrailNode(JSON.parse(this.actions[i].from).nodeId, JSON.parse(this.actions[i].from).nodeType, JSON.parse(this.actions[i].from).targetAnchor); let to = new TrailNode(JSON.parse(this.actions[i].to).nodeId, JSON.parse(this.actions[i].to).nodeType, JSON.parse(this.actions[i].to).targetAnchor); - let event = { "id": i, "type": this.actions[i].eventType, "properties": JSON.parse(this.actions[i].eventProperties), "timestamp": new Date(this.actions[i].hasTimestamp)} - let agent = this.actions[i].agent + let event = { "id": i, "type": this.actions[i].eventType, "properties": JSON.parse(this.actions[i].eventProperties), "timestamp": new Date(this.actions[i].hasTimestamp)}; + let agent = this.actions[i].agent; + + actionArray.push(new Action(from, to, event, agent)); + } + // create Action object from json obj + else if (this.actions[i] && this.actions[i].hasOwnProperty("from") && + this.actions[i].hasOwnProperty("to") && + this.actions[i].hasOwnProperty("agent") && + this.actions[i].hasOwnProperty("event")) + { + let from = new TrailNode(this.actions[i].from.nodeId, this.actions[i].from.nodeType, this.actions[i].from.targetAnchor); + let to = new TrailNode(this.actions[i].to.nodeId, this.actions[i].to.nodeType, this.actions[i].to.targetAnchor); + let event = { "id": this.actions[i].event.id, "type": this.actions[i].event.type, "properties": this.actions[i].event.properties, "timestamp": new Date(this.actions[i].event.timestamp)}; + let agent = this.actions[i].agent; actionArray.push(new Action(from, to, event, agent)); } - else if(this.actions[i].hasOwnProperty("hasTimestamp")) + else if(this.actions[i] && this.actions[i].hasOwnProperty("hasTimestamp")) { // all we got is event's id and timestamp actionIds.push(new Action(null, null, {"id": i, "timestamp": new Date(this.actions[i].hasTimestamp)}, null)); } } - if (actionArray.length>0) + if (actionArray.length > 0) { - this.actions = sort(actionArray); + this.actions = new List(...sort(actionArray)); } - else if (actionIds.length>0) + else if (actionIds.length > 0) { - actionIds = sort(actionIds); - - for (let i in actionIds) - { - this.actions.push(actionIds[i].id); - } + this.actions = new List(...sort(actionIds)); } - } function isValid(entity) @@ -314,24 +355,51 @@ function isValid(entity) function sort(actions = null) { - if (actions && Array.isArray(actions) && actions[0] instanceof Object) + if (actions && Array.isArray(actions) && actions[0].event.timestamp.constructor === Date) { // sort and return object array based on timestamp return actions.sort(function(action1, action2) { - return new Date(action1.event['timestamp']) - new Date(action2.event['timestamp']); + return action1.event['timestamp'] - action2.event['timestamp']; }); } - else if (actions && Array.isArray(actions) && this.actions[0] instanceof Action) + else if (actions && Array.isArray(actions) && actions[0].event.timestamp.constructor === String) { // sort Action array based on timestamp return actions.sort(function(action1, action2) { - return action1.event['timestamp'] - action2.event['timestamp']; + return new Date(action1.event['timestamp']) - new Date(action2.event['timestamp']); }); } } +Trail.prototype.toJSON = function () +{ + if (this.actions.constructor === List && this.actions.head && (!this.actions.head.from && !this.actions.head.to && !this.actions.head.agent)) + { + let actionArray = []; + let action = this.actions.head; + + while(action.next) + { + actionArray.push(action.id); + action = action.next; + } + + this.actions = actionArray; + } + + return { + id: this.id, + parent: this.parent, + properties: this.properties, + metaproperties: this.metaproperties, + interfaces: this.interfaces, + actions: this.actions, + type: this.type + }; +} + Trail.prototype.serialize = function () { return { @@ -340,7 +408,7 @@ Trail.prototype.serialize = function () properties: this.properties, metaproperties: this.metaproperties, interfaces: this.interfaces, - actions: this.actions, + actions: this.actions.toArray(), type: this.type }; } @@ -369,7 +437,7 @@ class TrailNode { class Action extends Item { constructor(from, to, event, agent) { - super() + super(); this.from = from; this.to = to; this.agent = agent; @@ -405,7 +473,12 @@ class Action extends Item { toString() { - return "[" + new Date(this.event['timestamp']).toISOString() + "] " + this.from + " -- " + this.event['eventId'] + " (by " + this.agent + ") --> " + this.to; + return JSON.stringify({ from: this.from, to: this.to, agent: this.agent, event: this.event, id: this.event.id }); + } + + toJSON() + { + return { from: this.from, to: this.to, agent: this.agent, event: this.event, id: this.event.id }; } } @@ -413,8 +486,4 @@ Trail.type = Types.TRAIL; Trail.isValid = isValid; Trail.sort = sort; -// Trail.Action = Action; -// Trail.TrailNode = TrailNode; - module.exports = Trail; - From 97b10dd9427a81a9cf4d4b56b421324cfbc337a9 Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Wed, 22 Sep 2021 17:53:15 -0300 Subject: [PATCH 10/24] Improving Action load with linked list --- trail.js | 62 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/trail.js b/trail.js index aec2536..cc9687d 100644 --- a/trail.js +++ b/trail.js @@ -29,7 +29,7 @@ function Trail (id, actions, parent) this.properties = trail.properties || {}; this.metaproperties = trail.metaproperties || {}; this.interfaces = trail.interfaces || {}; - this.actions = trail.actions || new List(); + this.actions = trail.actions || {}; if (this.actions && Object.keys(this.actions).length > 0) { loadActions.call(this); @@ -42,7 +42,7 @@ function Trail (id, actions, parent) this.interfaces = {}; this.properties = {}; this.metaproperties = {}; - this.actions = actions || new List(); + this.actions = actions || {}; } @@ -295,23 +295,10 @@ function loadActions(actions = null) let actionIds = [] for (let i in this.actions) { - // create Action object from hkb parsed data + // create Action object from json object if (this.actions[i] && this.actions[i].hasOwnProperty("from") && this.actions[i].hasOwnProperty("to") && this.actions[i].hasOwnProperty("agent") && - this.actions[i].hasOwnProperty("eventType")) - { - let from = new TrailNode(JSON.parse(this.actions[i].from).nodeId, JSON.parse(this.actions[i].from).nodeType, JSON.parse(this.actions[i].from).targetAnchor); - let to = new TrailNode(JSON.parse(this.actions[i].to).nodeId, JSON.parse(this.actions[i].to).nodeType, JSON.parse(this.actions[i].to).targetAnchor); - let event = { "id": i, "type": this.actions[i].eventType, "properties": JSON.parse(this.actions[i].eventProperties), "timestamp": new Date(this.actions[i].hasTimestamp)}; - let agent = this.actions[i].agent; - - actionArray.push(new Action(from, to, event, agent)); - } - // create Action object from json obj - else if (this.actions[i] && this.actions[i].hasOwnProperty("from") && - this.actions[i].hasOwnProperty("to") && - this.actions[i].hasOwnProperty("agent") && this.actions[i].hasOwnProperty("event")) { let from = new TrailNode(this.actions[i].from.nodeId, this.actions[i].from.nodeType, this.actions[i].from.targetAnchor); @@ -321,20 +308,26 @@ function loadActions(actions = null) actionArray.push(new Action(from, to, event, agent)); } - else if(this.actions[i] && this.actions[i].hasOwnProperty("hasTimestamp")) + else if(this.actions[i] && this.actions[i].hasOwnProperty("event") && this.actions[i].event.hasOwnProperty("timestamp")) + { + // we got event's id and timestamp + actionArray.push(new Action(null, null, {"id": i, "timestamp": new Date(this.actions[i].event.timestamp)}, null)); + } + else { - // all we got is event's id and timestamp - actionIds.push(new Action(null, null, {"id": i, "timestamp": new Date(this.actions[i].hasTimestamp)}, null)); + // all we got is event's id + actionIds.push(i); } } + // sort action items before creating list if (actionArray.length > 0) { this.actions = new List(...sort(actionArray)); } else if (actionIds.length > 0) { - this.actions = new List(...sort(actionIds)); + this.actions = actionIds; } } @@ -375,18 +368,27 @@ function sort(actions = null) Trail.prototype.toJSON = function () { - if (this.actions.constructor === List && this.actions.head && (!this.actions.head.from && !this.actions.head.to && !this.actions.head.agent)) + let actionArray = []; + + if (this.actions.constructor === List) { - let actionArray = []; - let action = this.actions.head; - - while(action.next) + //in case we have only action ids and timestamps + if(this.actions.head && (!this.actions.head.from && !this.actions.head.to && !this.actions.head.agent)) { - actionArray.push(action.id); - action = action.next; + let action = this.actions.head; + + while(action.next) + { + actionArray.push(action.id); + action = action.next; + } } - - this.actions = actionArray; + else { + actionArray = this.actions.toArray(); + } + } + else{ + actionArray = this.actions; } return { @@ -395,7 +397,7 @@ Trail.prototype.toJSON = function () properties: this.properties, metaproperties: this.metaproperties, interfaces: this.interfaces, - actions: this.actions, + actions: actionArray, type: this.type }; } From 8bf4efa44d40c899d3b811ad9df8d323f3c176df Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Fri, 24 Sep 2021 10:18:52 -0300 Subject: [PATCH 11/24] Improving action class and its loading and sorting --- trail.js | 83 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/trail.js b/trail.js index cc9687d..cbe6dc2 100644 --- a/trail.js +++ b/trail.js @@ -12,6 +12,7 @@ const {List, Item} = require('linked-list'); // const shortid = require('shortid'); const Types = require('./types'); const HKEntity = require('./hkentity'); +const e = require('express'); // const Connector = require('./connector'); // const Link = require('./link'); // const RoleTypes = require('./roletypes'); @@ -29,10 +30,10 @@ function Trail (id, actions, parent) this.properties = trail.properties || {}; this.metaproperties = trail.metaproperties || {}; this.interfaces = trail.interfaces || {}; - this.actions = trail.actions || {}; + this.actions = trail.actions || new List(); if (this.actions && Object.keys(this.actions).length > 0) { - loadActions.call(this); + this.loadActions(); } } else @@ -42,7 +43,7 @@ function Trail (id, actions, parent) this.interfaces = {}; this.properties = {}; this.metaproperties = {}; - this.actions = actions || {}; + this.actions = actions || new List(); } @@ -70,13 +71,12 @@ Trail.prototype.addAction = function (action) { if (new Date(current.event['timestamp']) - new Date(action.event['timestamp'])) { - current.prepend(action); - return; + return current.prepend(action); } current = current.next; } - this.actions.append(action); + return this.actions.append(action); } // Remove an action from trail using its reference @@ -98,6 +98,18 @@ Trail.prototype.removeAction = function (action) this.action.detach(); } +// `append` and `prepend` operations are +// handled by our linked list structure +Trail.prototype.append = function(action) +{ + return this.actions.append(action); +} + +Trail.prototype.prepend = function(action) +{ + return this.actions.prepend(action); +} + // aliases `in` and `out` represent the input and // output TrailNodes for a trail, which conceptually is // done through the use of Port elements in Hyperknowledge @@ -223,7 +235,7 @@ Trail.prototype.getPositionOf = function(action){ //search action by eventId var array = this.actions.toArray(); for (var i = 0; i < this.actions.size; i++) { - if(array[i].event["eventId"] == action.event["eventId"]){ + if(array[i].event["id"] == action.event["id"]){ return i; } } @@ -261,7 +273,7 @@ Trail.prototype.search = function(eventId = null, filters){ var array = this.actions.toArray(); if (eventId){ for (var i = 0; i < this.actions.size; i++) { - if(array[i].event["eventId"] == eventId){ + if(array[i].event["id"] == eventId){ return array[i]; } } @@ -281,14 +293,20 @@ Trail.prototype.search = function(eventId = null, filters){ return resultSet } -function loadActions(actions = null) +Trail.prototype.loadActions = function (actions = null) { // use array of actions if passed if(actions && Array.isArray(actions)) { - this.actions = new List(...sort(actions)); - return; + if(actions[0].event.timestamp) + { + this.actions = new List(...sort(actions)); + } + else { + this.actions = new List(...actions); + } + return this.actions; } let actionArray = [] @@ -306,12 +324,12 @@ function loadActions(actions = null) let event = { "id": this.actions[i].event.id, "type": this.actions[i].event.type, "properties": this.actions[i].event.properties, "timestamp": new Date(this.actions[i].event.timestamp)}; let agent = this.actions[i].agent; - actionArray.push(new Action(from, to, event, agent)); + actionArray.push(new Action({from, to, event, agent})); } else if(this.actions[i] && this.actions[i].hasOwnProperty("event") && this.actions[i].event.hasOwnProperty("timestamp")) { // we got event's id and timestamp - actionArray.push(new Action(null, null, {"id": i, "timestamp": new Date(this.actions[i].event.timestamp)}, null)); + actionArray.push(new Action({event: {"id": i, "timestamp": new Date(this.actions[i].event.timestamp)}})); } else { @@ -356,7 +374,7 @@ function sort(actions = null) return action1.event['timestamp'] - action2.event['timestamp']; }); } - else if (actions && Array.isArray(actions) && actions[0].event.timestamp.constructor === String) + else if (actions && Array.isArray(actions) && (actions[0].event.timestamp.constructor === String || actions[0].event.timestamp.constructor === Number)) { // sort Action array based on timestamp return actions.sort(function(action1, action2) @@ -376,8 +394,8 @@ Trail.prototype.toJSON = function () if(this.actions.head && (!this.actions.head.from && !this.actions.head.to && !this.actions.head.agent)) { let action = this.actions.head; - - while(action.next) + + while(action) { actionArray.push(action.id); action = action.next; @@ -387,7 +405,8 @@ Trail.prototype.toJSON = function () actionArray = this.actions.toArray(); } } - else{ + else if (Array.isArray(this.actions)) + { actionArray = this.actions; } @@ -437,7 +456,7 @@ class TrailNode { // nodes (along with their respective target anchors), // the related event and an agent (user, system or content). class Action extends Item { - constructor(from, to, event, agent) + constructor({from = null, to = null, event = {}, agent = null} = {}) { super(); this.from = from; @@ -445,16 +464,12 @@ class Action extends Item { this.agent = agent; this.event = event; - if(!this.event) - { - return - } // create timestamp if needed - if(!this.event['timestamp']) - { - this.event['timestamp'] = new Date().getTime(); - } + // if(!this.event['timestamp']) + // { + // this.event['timestamp'] = new Date().getTime(); + // } // get event id or create a new one if(!this.event['id'] || this.event['id'] == '') @@ -475,17 +490,33 @@ class Action extends Item { toString() { + if(this.from && this.to && this.agent && this.event && this.event.id ) + { return JSON.stringify({ from: this.from, to: this.to, agent: this.agent, event: this.event, id: this.event.id }); + } + else + { + return this.event.id; + } } toJSON() { + if(this.from && this.to && this.agent && this.event && this.event.id ) + { return { from: this.from, to: this.to, agent: this.agent, event: this.event, id: this.event.id }; + } + else + { + return this.event.id; + } } } Trail.type = Types.TRAIL; Trail.isValid = isValid; Trail.sort = sort; +Trail.Action = Action; +Trail.TrailNode = TrailNode; module.exports = Trail; From 07c724cdc8cf36026befda1059c4f2ee6de930c4 Mon Sep 17 00:00:00 2001 From: Rafael Brandao Date: Sat, 25 Sep 2021 19:55:30 -0300 Subject: [PATCH 12/24] Removing unused require --- trail.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/trail.js b/trail.js index cbe6dc2..cd95480 100644 --- a/trail.js +++ b/trail.js @@ -6,16 +6,10 @@ 'use strict' const {List, Item} = require('linked-list'); -// const List = Linked.List; -// const Item = Linked.Item; // const shortid = require('shortid'); const Types = require('./types'); const HKEntity = require('./hkentity'); -const e = require('express'); -// const Connector = require('./connector'); -// const Link = require('./link'); -// const RoleTypes = require('./roletypes'); function Trail (id, actions, parent) From 8892edf49e764cd83f40966719fa65652cd3ca89 Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Wed, 24 Nov 2021 13:09:42 -0300 Subject: [PATCH 13/24] Added actions to hkgraph --- hkgraph.js | 24 ++++++++++++++++++++---- index.js | 1 + trail.js | 7 ++++++- types.js | 1 + 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/hkgraph.js b/hkgraph.js index 463b4f1..f5dc1cd 100644 --- a/hkgraph.js +++ b/hkgraph.js @@ -9,10 +9,11 @@ const Connector = require("./connector"); const Context = require("./context"); const Link = require("./link"); const Node = require("./node"); -const Reference = require("./reference"); +const Reference = require("./reference"); const Trail = require("./trail"); const Types = require("./types"); const shortid = require('shortid'); +const { Action } = require("./trail"); function HKGraph() { @@ -22,6 +23,7 @@ function HKGraph() this.connectors = {}; this.refs = {}; this.trails = {}; + this.actions = {}; // Auxiliar maps this.bindsMap = {}; @@ -43,7 +45,8 @@ HKGraph.prototype.hasId = function(id) this.links.hasOwnProperty(id) || this.connectors.hasOwnProperty(id) || this.refs.hasOwnProperty(id) || - this.trails.hasOwnProperty(id); + this.trails.hasOwnProperty(id) || + this.actions.hasOwnProperty(id); }; @@ -72,7 +75,7 @@ HKGraph.prototype.setEntity = function(entity) oldEntity.className = entity.className; } - if(entity.type === Types.NODE || entity.type === Types.TRAIL || entity.type === Types.REFERENCE || entity.type === Types.CONTEXT) + if(entity.type === Types.NODE || entity.type === Types.TRAIL || entity.type === Types.REFERENCE || entity.type === Types.CONTEXT || entity.type === Types.ACTION) { oldEntity.interfaces = entity.interfaces; @@ -190,6 +193,15 @@ HKGraph.prototype.addEntity = function(entity) } break; } + case Types.ACTION: + { + if(entity instanceof Trail.Action) + { + newEntity = entity; + this.actions[entity.id] = newEntity; + } + break; + } case Types.LINK: { if (Link.isValid(entity)) @@ -350,6 +362,10 @@ HKGraph.prototype.removeEntity = function(id) delete this.compositeNodeMap[entity.id]; break; } + case Action.type: + { + delete this.actions[id]; + } } if(this.orphans.hasOwnProperty(entity.parent)) @@ -480,7 +496,7 @@ HKGraph.prototype.getEntity = function(id) c.id = null; return c; } - return this.nodes[id] || this.contexts[id] || this.links[id] || this.connectors[id] || this.refs[id] || this.trails[id] || null; + return this.nodes[id] || this.contexts[id] || this.links[id] || this.connectors[id] || this.refs[id] || this.trails[id] || this.actions[id] || null; }; HKGraph.prototype.getEntities = function() diff --git a/index.js b/index.js index 6d084e4..bedb372 100644 --- a/index.js +++ b/index.js @@ -26,6 +26,7 @@ exports.CONNECTOR_TYPE = Types.CONNECTOR; exports.BIND_TYPE = Types.BIND; exports.INTERFACE = Types.INTERFACE; exports.TRAIL_TYPE = Types.TRAIL; +exports.ACTION_TYPE = Types.ACTION; const RolesTypes = require("./roletypes"); diff --git a/trail.js b/trail.js index cd95480..05da606 100644 --- a/trail.js +++ b/trail.js @@ -325,6 +325,11 @@ Trail.prototype.loadActions = function (actions = null) // we got event's id and timestamp actionArray.push(new Action({event: {"id": i, "timestamp": new Date(this.actions[i].event.timestamp)}})); } + else if(Array.isArray(this.actions)) + { + // all we got is an Array with the event's id + actionIds.push(this.actions[i]); + } else { // all we got is event's id @@ -457,7 +462,7 @@ class Action extends Item { this.to = to; this.agent = agent; this.event = event; - + this.type = Types.ACTION; // create timestamp if needed // if(!this.event['timestamp']) diff --git a/types.js b/types.js index 1f87e69..8c75ec6 100644 --- a/types.js +++ b/types.js @@ -14,6 +14,7 @@ exports.REFERENCE = 'ref'; exports.INTERFACE = 'interface'; exports.BIND = 'bind'; exports.TRAIL = 'trail'; +exports.ACTION = 'action'; exports.isValidType = function (type) { From f1af0da1bcf6dc025be93e15e65e1db54895766d Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Mon, 13 Dec 2021 15:35:10 -0300 Subject: [PATCH 14/24] this.actions.toArray is not a function bugfix --- trail.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/trail.js b/trail.js index 05da606..6d15191 100644 --- a/trail.js +++ b/trail.js @@ -422,13 +422,23 @@ Trail.prototype.toJSON = function () Trail.prototype.serialize = function () { + let actions; + if(this.actions instanceof Array) + { + actions = this.actions; + } + else + { + actions = this.actions.toArray(); + } + return { id: this.id, parent: this.parent, properties: this.properties, metaproperties: this.metaproperties, interfaces: this.interfaces, - actions: this.actions.toArray(), + actions: actions, type: this.type }; } From ba8161d6a9d56166cc03275c6ab1427556229b82 Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Wed, 19 Jan 2022 18:00:29 -0300 Subject: [PATCH 15/24] Bugfix Trail.out not working with only one action --- trail.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/trail.js b/trail.js index 6d15191..01ec7cb 100644 --- a/trail.js +++ b/trail.js @@ -117,12 +117,22 @@ Trail.prototype.in = function(from = null) { } Trail.prototype.out = function(to = null) { + if(this.actions.tail) + { + if (to) + { + this.actions.tail.to = to; + } + + return this.actions.tail.to; + } + if (to) { - this.actions.tail.to = to; + this.actions.head.to = to; } - return this.actions.tail.to; + return this.actions.head.to; } Trail.prototype.size = function() From 43823a4c55391b6e03f6f98d6fc948fea2ee7c9c Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Tue, 28 Jun 2022 17:50:49 -0300 Subject: [PATCH 16/24] Merge bugfix --- src/hkgraph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hkgraph.js b/src/hkgraph.js index 1e8e20f..88ab655 100644 --- a/src/hkgraph.js +++ b/src/hkgraph.js @@ -86,7 +86,7 @@ class HKGraph oldEntity.className = entity.className; } - if (entity.type === HKTypes.NODE || entity.type === Types.TRAIL || entity.type === HKTypes.REFERENCE || entity.type === HKTypes.CONTEXT || entity.type === HKTypes.VIRTUAL_NODE || entity.type === HKTypes.VIRTUAL_CONTEXT || entity.type === Types.ACTION) + if (entity.type === HKTypes.NODE || entity.type === HKTypes.TRAIL || entity.type === HKTypes.REFERENCE || entity.type === HKTypes.CONTEXT || entity.type === HKTypes.VIRTUAL_NODE || entity.type === HKTypes.VIRTUAL_CONTEXT || entity.type === HKTypes.ACTION) { oldEntity.interfaces = entity.interfaces; } From 32c887096de2a8cee1e48273378e2551baf23212 Mon Sep 17 00:00:00 2001 From: Elton Soares Date: Wed, 29 Jun 2022 18:30:23 -0300 Subject: [PATCH 17/24] Identation fix --- src/types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.js b/src/types.js index e1f6a95..897ae77 100644 --- a/src/types.js +++ b/src/types.js @@ -13,7 +13,7 @@ exports.REFERENCE = 'ref'; exports.INTERFACE = 'interface'; exports.BIND = 'bind'; exports.TRAIL = 'trail'; -exports.ACTION = 'action'; +exports.ACTION = 'action'; exports.VIRTUAL_NODE = 'virtualnode'; exports.VIRTUAL_CONTEXT = 'virtualcontext'; exports.VIRTUAL_LINK = 'virtuallink'; From 66c0f805e3a4b9e9687db75dbe1bc6b4e9dd305a Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Thu, 30 Jun 2022 16:59:03 -0300 Subject: [PATCH 18/24] Added linked-list to HKLib --- package.json | 2 +- src/linked-list/index.cjs | 290 +++++++++++++++++++++++++++ src/linked-list/index.d.ts | 29 +++ src/linked-list/license | 22 +++ src/linked-list/package.json | 115 +++++++++++ src/linked-list/readme.md | 368 +++++++++++++++++++++++++++++++++++ 6 files changed, 825 insertions(+), 1 deletion(-) create mode 100644 src/linked-list/index.cjs create mode 100644 src/linked-list/index.d.ts create mode 100644 src/linked-list/license create mode 100644 src/linked-list/package.json create mode 100644 src/linked-list/readme.md diff --git a/package.json b/package.json index 9e22a0c..3820d7c 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "request": "^2.88.0", "request-promise-native": "^1.0.8", "shortid": "^2.2.14", - "linked-list": "git+ssh://git@github.ibm.com:keg-core/linked-list.git" + "linked-list": "file:src/linked-list" }, "license": "MIT", "repository": { diff --git a/src/linked-list/index.cjs b/src/linked-list/index.cjs new file mode 100644 index 0000000..5dc2d37 --- /dev/null +++ b/src/linked-list/index.cjs @@ -0,0 +1,290 @@ +// Creates a new `Iterator` for looping over the `List`. +class Iterator { + constructor(item) { + this.item = item + } + + // Move the `Iterator` to the next item. + next() { + this.value = this.item + this.done = !this.item + this.item = this.item ? this.item.next : undefined + return this + } +} + +// Creates a new `Item`: +// An item is a bit like DOM node: It knows only about its “parent” (`list`), +// the item before it (`prev`), and the item after it (`next`). +class Item { + // Prepends the given item *before* the item operated on. + prepend(item) { + var list = this.list + + if (!item || !item.append || !item.prepend || !item.detach) { + throw new Error( + 'An argument without append, prepend, or detach methods was given to `Item#prepend`.' + ) + } + + // If self is detached, return false. + if (!list) { + return false + } + + // Detach the prependee. + item.detach() + + // If self has a previous item... + if (this.prev) { + item.prev = this.prev + this.prev.next = item + } + + // Connect the prependee. + item.next = this + item.list = list + + // Set the previous item of self to the prependee. + this.prev = item + + // If self is the first item in the parent list, link the lists first item to + // the prependee. + if (this === list.head) { + list.head = item + } + + // If the the parent list has no last item, link the lists last item to self. + if (!list.tail) { + list.tail = this + } + + list.size++ + + return item + } + + // Appends the given item *after* the item operated on. + append(item) { + var list = this.list + + if (!item || !item.append || !item.prepend || !item.detach) { + throw new Error( + 'An argument without append, prepend, or detach methods was given to `Item#append`.' + ) + } + + if (!list) { + return false + } + + // Detach the appendee. + item.detach() + + // If self has a next item… + if (this.next) { + item.next = this.next + this.next.prev = item + } + + // Connect the appendee. + item.prev = this + item.list = list + + // Set the next item of self to the appendee. + this.next = item + + // If the the parent list has no last item or if self is the parent lists last + // item, link the lists last item to the appendee. + if (this === list.tail || !list.tail) { + list.tail = item + } + + list.size++ + + return item + } + + // Detaches the item operated on from its parent list. + detach() { + var list = this.list + + if (!list) { + return this + } + + // If self is the last item in the parent list, link the lists last item to + // the previous item. + if (list.tail === this) { + list.tail = this.prev + } + + // If self is the first item in the parent list, link the lists first item to + // the next item. + if (list.head === this) { + list.head = this.next + } + + // If both the last and first items in the parent list are the same, remove + // the link to the last item. + if (list.tail === list.head) { + list.tail = null + } + + // If a previous item exists, link its next item to selfs next item. + if (this.prev) { + this.prev.next = this.next + } + + // If a next item exists, link its previous item to selfs previous item. + if (this.next) { + this.next.prev = this.prev + } + + // Remove links from self to both the next and previous items, and to the + // parent list. + this.prev = this.next = this.list = null + + list.size-- + + return this + } +} + +Item.prototype.next = Item.prototype.prev = Item.prototype.list = null + +// Creates a new List: A linked list is a bit like an Array, but knows nothing +// about how many items are in it, and knows only about its first (`head`) and +// last (`tail`) items. +// Each item (e.g. `head`, `tail`, &c.) knows which item comes before or after +// it (its more like the implementation of the DOM in JavaScript). +class List { + // Creates a new list from the arguments (each a list item) passed in. + static of(...items) { + return appendAll(new this(), items) + } + + // Creates a new list from the given array-like object (each a list item) passed + // in. + static from(items) { + return appendAll(new this(), items) + } + + constructor(...items) { + appendAll(this, items) + } + + // Returns the list’s items as an array. + // This does *not* detach the items. + toArray() { + var item = this.head + var result = [] + + while (item) { + result.push(item) + item = item.next + } + + return result + } + + // Prepends the given item to the list. + // `item` will be the new first item (`head`). + prepend(item) { + if (!item) { + return false + } + + if (!item.append || !item.prepend || !item.detach) { + throw new Error( + 'An argument without append, prepend, or detach methods was given to `List#prepend`.' + ) + } + + if (this.head) { + return this.head.prepend(item) + } + + item.detach() + item.list = this + this.head = item + this.size++ + + return item + } + + // Appends the given item to the list. + // `item` will be the new last item (`tail`) if the list had a first item, and + // its first item (`head`) otherwise. + append(item) { + if (!item) { + return false + } + + if (!item.append || !item.prepend || !item.detach) { + throw new Error( + 'An argument without append, prepend, or detach methods was given to `List#append`.' + ) + } + + // If self has a last item, defer appending to the last items append method, + // and return the result. + if (this.tail) { + return this.tail.append(item) + } + + // If self has a first item, defer appending to the first items append method, + // and return the result. + if (this.head) { + return this.head.append(item) + } + + // …otherwise, there is no `tail` or `head` item yet. + item.detach() + item.list = this + this.head = item + this.size++ + + return item + } + + // Creates an iterator from the list. + [Symbol.iterator]() { + return new Iterator(this.head) + } +} + +List.prototype.size = 0 +List.prototype.tail = List.prototype.head = null + +// Creates a new list from the items passed in. +function appendAll(list, items) { + var index + var item + var iterator + + if (!items) { + return list + } + + if (items[Symbol.iterator]) { + iterator = items[Symbol.iterator]() + item = {} + + while (!item.done) { + item = iterator.next() + list.append(item && item.value) + } + } else { + index = -1 + + while (++index < items.length) { + list.append(items[index]) + } + } + + return list +} + +module.exports.Item = Item; +module.exports.List = List; \ No newline at end of file diff --git a/src/linked-list/index.d.ts b/src/linked-list/index.d.ts new file mode 100644 index 0000000..4cfe851 --- /dev/null +++ b/src/linked-list/index.d.ts @@ -0,0 +1,29 @@ +export namespace List{ + export class Item { + prev: this + next: this + list: List + + detach(): this + prepend(item: T): T + append(item: T): T + } +} +export class List implements Iterable { + static of(...items: T[]): List + static from(items: Iterable): List + + head: T | null + tail: T | null + size: number + + constructor(...items: T[]) + toArray(): T[] + prepend(item: T): T + append(item: T): T + [Symbol.iterator](): Iterator +} + +export type Item = List.Item; + + diff --git a/src/linked-list/license b/src/linked-list/license new file mode 100644 index 0000000..0c06d5b --- /dev/null +++ b/src/linked-list/license @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Titus Wormer + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/linked-list/package.json b/src/linked-list/package.json new file mode 100644 index 0000000..8a80e09 --- /dev/null +++ b/src/linked-list/package.json @@ -0,0 +1,115 @@ +{ + "_from": "git+ssh://git@github.ibm.com:keg-core/linked-list.git", + "_id": "linked-list@3.0.1", + "_inBundle": false, + "_integrity": "", + "_location": "/linked-list", + "_phantomChildren": {}, + "_requested": { + "type": "git", + "raw": "linked-list@git+ssh://git@github.ibm.com:keg-core/linked-list.git", + "name": "linked-list", + "escapedName": "linked-list", + "rawSpec": "git+ssh://git@github.ibm.com:keg-core/linked-list.git", + "saveSpec": "git+ssh://git@github.ibm.com:keg-core/linked-list.git", + "fetchSpec": "git@github.ibm.com:keg-core/linked-list.git", + "gitCommittish": null + }, + "_requiredBy": [ + "/" + ], + "_resolved": "git+ssh://git@github.ibm.com:keg-core/linked-list.git#778981762dbdcbf5f10b031345ce9756c29e0915", + "_spec": "linked-list@git+ssh://git@github.ibm.com:keg-core/linked-list.git", + "_where": "/Users/gabrielapinheiro/ghe/keg/hklib", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/linked-list/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + { + "name": "Blake Embrey", + "email": "hello@blakeembrey.com" + }, + { + "name": "Regev Brody", + "email": "regevbr@gmail.com" + } + ], + "deprecated": false, + "description": "Minimalistic linked lists", + "devDependencies": { + "@types/tape": "^4.0.0", + "c8": "^7.0.0", + "prettier": "^2.0.0", + "remark-cli": "^9.0.0", + "remark-preset-wooorm": "^8.0.0", + "tape": "^5.0.0", + "xo": "^0.38.0" + }, + "files": [ + "index.cjs", + "index.d.ts" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/linked-list#readme", + "keywords": [ + "double", + "linked", + "list" + ], + "license": "MIT", + "main": "index.cjs", + "name": "linked-list", + "prettier": { + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "bracketSpacing": false, + "semi": false, + "trailingComma": "none" + }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/linked-list.git" + }, + "scripts": { + "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", + "test": "npm run format && npm run test-coverage", + "test-api": "node test.js", + "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js" + }, + "sideEffects": false, + "type": "module", + "types": "index.d.ts", + "version": "3.0.1", + "xo": { + "prettier": true, + "rules": { + "no-var": "off", + "prefer-arrow-callback": "off", + "no-multi-assign": "off" + }, + "ignores": [ + "*.ts", + "linked-list.js" + ] + } +} diff --git a/src/linked-list/readme.md b/src/linked-list/readme.md new file mode 100644 index 0000000..20321ea --- /dev/null +++ b/src/linked-list/readme.md @@ -0,0 +1,368 @@ +# linked-list + +[![Build][build-badge]][build] +[![Coverage][coverage-badge]][coverage] +[![Downloads][downloads-badge]][downloads] +[![Size][size-badge]][size] + +Small double [linked list][wiki]. + +## Install + +This package is ESM only: Node 12+ is needed to use it and it must be `import`ed +instead of `require`d. + +Edit: We forked this opensource lib and modified it so we could use it with `require`, since some HK images (e.g. KES) are still based on Node 12 and all of them use required instead of import. + +[npm][]: + +```sh +npm install linked-list +``` + +## Use + +```js +import {List, Item} from 'linked-list' + +var item1 = new Item() +var item2 = new Item() +var item3 = new Item() +var list = new List(item1, item2, item3) + +list.head // => item1 +list.head.next // => item2 +list.head.next.next // => item3 +list.head.next.prev // => item1 +list.tail // => item3 +list.tail.next // => `null` +``` + +Subclassing: + +```js +import {List, Item} from 'linked-list' + +class Tokens extends List { + join(delimiter) { + return this.toArray().join(delimiter) + } +} + +class Token extends Item { + constructor(value) { + super() + this.value = value + } + + toString() { + return this.value + } +} + +var dogs = new Token('dogs') +var and = new Token('&') +var cats = new Token('cats') +var tokens = new Tokens(dogs, and, cats) + +console.log(tokens.join(' ')) // => 'dogs & cats' + +and.prepend(cats) +and.append(dogs) + +console.log(tokens.join(' ') + '!') // => 'cats & dogs!' +``` + +## API + +This package exports the following identifiers: `List`, `Item`. +There is no default export. + +### `List([items…])` + +```js +new List() +new List(new Item(), new Item()) +``` + +Create a new linked list. + +#### `List.from([items])` + +```js +List.from() +List.from([]) +List.from([new Item(), new Item()]) +``` + +Create a new `this` and adds the given array of items. +Ignores `null` or `undefined` values. +Throws an error when a given item has no `detach`, `append`, or `prepend` +methods. + +#### `List.of([items…])` + +```js +List.of() +List.of(new Item(), new Item()) +``` + +Creates a new linked list from the given arguments. +Defers to `List.from`. + +#### `List#append(item)` + +```js +var list = new List() +var item = new Item() + +list.head === null // => true +item.list === null // => true + +list.append(item) + +list.head === item // => true +item.list === list // => true +``` + +Appends an item to a list. +Throws an error when the given item has no `detach`, `append`, or `prepend` +methods. +Returns the given item. + +#### `List#prepend(item)` + +```js +var list = new List() +var item = new Item() + +list.prepend(item) +``` + +Prepends an item to a list. +Throws an error when the given item has no `detach`, `append`, or `prepend` +methods. +Returns the given item. + +#### `List#toArray()` + +```js +var item1 = new Item() +var item2 = new Item() +var list = new List(item1, item2) +var array = list.toArray() + +array[0] === item1 // => true +array[1] === item2 // => true +array[0].next === item2 // => true +array[1].prev === item1 // => true +``` + +Returns the items in the list in an array. + +#### `List#head` + +```js +var item = new Item() +var list = new List(item) + +list.head === item // => true +``` + +The first item in a list, and `null` otherwise. + +#### `List#tail` + +```js +var list = new List() +var item1 = new Item() +var item2 = new Item() + +list.tail === null // => true + +list.append(item1) +list.tail === null // => true, see note. + +list.append(item2) +list.tail === item2 // => true +``` + +The last item in a list, and `null` otherwise. +Note that a list with only one item has **no tail**, only a head. + +#### `List#size` + +```js +var list = new List() +var item1 = new Item() +var item2 = new Item() + +list.size === 0 // => true + +list.append(item1) +list.size === 1 // => true + +list.append(item2) +list.size === 2 // => true +``` + +The number of items in the list. + +### `Item()` + +```js +var item = new Item() +``` + +Creates a new linked list Item. + +#### `Item#append(item)` + +```js +var item1 = new Item() +var item2 = new Item() + +new List().append(item1) + +item1.next === null // => true + +item1.append(item2) +item1.next === item2 // => true +``` + +Adds the given item **after** the operated on item in a list. +Throws an error when the given item has no `detach`, `append`, or `prepend` +methods. +Returns false when the operated on item is not attached to a list, otherwise the +given item. + +#### `Item#prepend(item)` + +```js +var item1 = new Item() +var item2 = new Item() + +new List().append(item1) + +item1.prev === null // => true + +item1.prepend(item2) +item1.prev === item2 // => true +``` + +Adds the given item **before** the operated on item in a list. +Throws an error when the given item has no `detach`, `append`, or `prepend` +methods. +Returns false when the operated on item is not attached to a list, otherwise +the given item. + +#### `Item#detach()` + +```js +var item = new Item() +var list = new List(item) + +item.list === list // => true + +item.detach() +item.list === null // => true +``` + +Removes the operated on item from its parent list. +Removes references to it on its parent `list`, and `prev` and `next` items; +relinking them when possible. +Returns the operated on item. +Even when it was already detached. + +#### `Item#next` + +```js +var item1 = new Item() +var item2 = new Item() + +new List(item1) + +item1.next === null // => true +item2.next === null // => true + +item1.append(item2) + +item1.next === item2 // => true + +item1.detach() + +item1.next === null // => true +``` + +The items succeeding item, and `null` otherwise. + +#### `Item#prev` + +```js +var item1 = new Item() +var item2 = new Item() + +new List(item) + +item1.prev === null // => true +item2.prev === null // => true + +item1.append(item2) + +item1.prev === item1 // => true + +item2.detach() + +item2.prev === null // => true +``` + +The items preceding item, and `null` otherwise. + +#### `Item#list` + +```js +var item = new Item() +var list = new List() + +item.list === null // => true + +list.append(item) + +item.list === list // => true + +item.detach() + +item.list === null // => true +``` + +The items parent list, and `null` otherwise. + +## License + +[MIT][license] © [Titus Wormer][author] + + + +[build-badge]: https://github.com/wooorm/linked-list/workflows/main/badge.svg + +[build]: https://github.com/wooorm/linked-list/actions + +[coverage-badge]: https://img.shields.io/codecov/c/github/wooorm/linked-list.svg + +[coverage]: https://codecov.io/github/wooorm/linked-list + +[downloads-badge]: https://img.shields.io/npm/dm/linked-list.svg + +[downloads]: https://www.npmjs.com/package/linked-list + +[size-badge]: https://img.shields.io/bundlephobia/minzip/linked-list.svg + +[size]: https://bundlephobia.com/result?p=linked-list + +[npm]: https://docs.npmjs.com/cli/install + +[license]: license + +[author]: https://wooorm.com + +[wiki]: https://wikipedia.org/wiki/Linked_list From 9e6f618ccf4dc788ce7b9d64b454b0d2c1c43f9d Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Thu, 30 Jun 2022 17:19:49 -0300 Subject: [PATCH 19/24] Trail build bugfix --- src/trail.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/trail.js b/src/trail.js index 01ec7cb..7a8e8c5 100644 --- a/src/trail.js +++ b/src/trail.js @@ -45,7 +45,6 @@ function Trail (id, actions, parent) } Trail.prototype = Object.create (HKEntity.prototype); -Trail.prototype.constructor = Trail; // Trail.prototype = Object.assign(Trail.prototype, List.prototype); // Multiple inheritance with assign From 7d229eb067230e61b2cc913ca0b6d42e0e158e36 Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Fri, 1 Jul 2022 14:26:04 -0300 Subject: [PATCH 20/24] fixed indentation --- src/datasource/hkdatasource.js | 89 +++++++++++++++++----------------- src/trail.js | 46 +++++++++--------- 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/datasource/hkdatasource.js b/src/datasource/hkdatasource.js index 2aa4b2f..54bd3c1 100644 --- a/src/datasource/hkdatasource.js +++ b/src/datasource/hkdatasource.js @@ -585,51 +585,52 @@ class HKDatasource } /** - * @callback GetTrailCallback - * @param {string} err An error object that indicate if the operation was succesful or not - * @param {object} trail - */ -/** - * Fetch trail - * - * @param {string} trailId The trail id to retrieve their nested entities. - * @param {GetTrailCallback} callback Callback with the entities - */ + * @callback GetTrailCallback + * @param {string} err An error object that indicate if the operation was succesful or not + * @param {object} trail + */ -fetchTrail(trailId, callback = () => {}) - { - let url = this.url + "repository/" + this.graphName + "/trail/" + encodeURIComponent(trailId); - - request.get(url, this.options, (err, res) => - { - // console.log(res.body); - if (!err) - { - if (requestCompletedWithSuccess(res.statusCode)) - { - try - { - let entities = convertEntities(res.body); - callback(null, entities[trailId]); - } - catch (exp) - { - callback(exp); - } - } - - else - { - callback(`Server responded with ${res.statusCode}. ${res.body}`); - } - } - - else - { - callback(err); - } - }); - } + /** + * Fetch trail + * + * @param {string} trailId The trail id to retrieve their nested entities. + * @param {GetTrailCallback} callback Callback with the entities + */ + + fetchTrail(trailId, callback = () => {}) + { + let url = this.url + "repository/" + this.graphName + "/trail/" + encodeURIComponent(trailId); + + request.get(url, this.options, (err, res) => + { + // console.log(res.body); + if (!err) + { + if (requestCompletedWithSuccess(res.statusCode)) + { + try + { + let entities = convertEntities(res.body); + callback(null, entities[trailId]); + } + catch (exp) + { + callback(exp); + } + } + + else + { + callback(`Server responded with ${res.statusCode}. ${res.body}`); + } + } + + else + { + callback(err); + } + }); + } /** * Filter entities using CSS pattern `(TODO: document it better)` diff --git a/src/trail.js b/src/trail.js index 7a8e8c5..126f96a 100644 --- a/src/trail.js +++ b/src/trail.js @@ -13,35 +13,35 @@ const HKEntity = require('./hkentity'); function Trail (id, actions, parent) - { +{ - if(arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) - { - let trail = arguments[0]; + if(arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) + { + let trail = arguments[0]; - this.id = trail.id || null; - this.parent = trail.parent || null; - this.properties = trail.properties || {}; - this.metaproperties = trail.metaproperties || {}; - this.interfaces = trail.interfaces || {}; - this.actions = trail.actions || new List(); + this.id = trail.id || null; + this.parent = trail.parent || null; + this.properties = trail.properties || {}; + this.metaproperties = trail.metaproperties || {}; + this.interfaces = trail.interfaces || {}; + this.actions = trail.actions || new List(); - if (this.actions && Object.keys(this.actions).length > 0) { - this.loadActions(); - } + if (this.actions && Object.keys(this.actions).length > 0) { + this.loadActions(); } - else - { - this.id = id || null; - this.parent = parent || null; - this.interfaces = {}; - this.properties = {}; - this.metaproperties = {}; - this.actions = actions || new List(); + } + else + { + this.id = id || null; + this.parent = parent || null; + this.interfaces = {}; + this.properties = {}; + this.metaproperties = {}; + this.actions = actions || new List(); - } + } - this.type = Types.TRAIL; + this.type = Types.TRAIL; } Trail.prototype = Object.create (HKEntity.prototype); From f0ab249049d1e812fa9c3942561b676ac59906b4 Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Fri, 1 Jul 2022 14:28:14 -0300 Subject: [PATCH 21/24] mend --- dist/connector.d.ts | 4 +- dist/connectorclass.d.ts | 14 +- dist/constants.d.ts | 4 +- dist/context.d.ts | 3 +- dist/datasource/hkdatasource.d.ts | 12 + dist/datasource/hkdatasource.js | 34 ++ dist/datasource/observer/clients/index.d.ts | 8 +- .../clients/rabbitmqobserverclient.d.ts | 2 + .../observer/clients/restobserverclient.d.ts | 1 + dist/fi/fioperator.d.ts | 4 +- dist/hkgraph.d.ts | 17 +- dist/hkgraph.js | 27 +- dist/index.js | 1 + dist/link.d.ts | 4 +- dist/node.d.ts | 10 +- dist/reference.d.ts | 9 +- dist/roletypes.d.ts | 10 +- dist/trail.d.ts | 78 ++- dist/trail.js | 483 ++++++++++++++---- dist/types.d.ts | 25 +- dist/types.js | 1 + dist/virtualcontext.js | 2 +- dist/virtuallink.js | 2 +- dist/virtualnode.js | 2 +- 24 files changed, 600 insertions(+), 157 deletions(-) diff --git a/dist/connector.d.ts b/dist/connector.d.ts index e94e3af..537f136 100644 --- a/dist/connector.d.ts +++ b/dist/connector.d.ts @@ -48,6 +48,8 @@ declare class Connector extends HKEntity { public roles: { [x: string]: string; }; + properties: any; + metaProperties: any; /** * Adds a new role to this connector. * @@ -96,6 +98,6 @@ declare class Connector extends HKEntity { }; } declare namespace Connector { - const type: string; + const type: "connector"; } import HKEntity = require("./hkentity"); diff --git a/dist/connectorclass.d.ts b/dist/connectorclass.d.ts index e271c8f..14286dd 100644 --- a/dist/connectorclass.d.ts +++ b/dist/connectorclass.d.ts @@ -2,10 +2,10 @@ * Copyright (c) 2016-present, IBM Research * Licensed under The MIT License [see LICENSE for details] */ -export var HIERARCHY: string; -export var FACTS: string; -export var REASONING: string; -export var CONSTRAINT: string; -export var CAUSAL: string; -export var POSSIBILITY: string; -export var POSSIBILITYRESOLVER: string; +export const HIERARCHY: "h"; +export const FACTS: "f"; +export const REASONING: "r"; +export const CONSTRAINT: "c"; +export const CAUSAL: "C"; +export const POSSIBILITY: "p"; +export const POSSIBILITYRESOLVER: "P"; diff --git a/dist/constants.d.ts b/dist/constants.d.ts index 1b39eba..3e75fe1 100644 --- a/dist/constants.d.ts +++ b/dist/constants.d.ts @@ -2,5 +2,5 @@ * Copyright (c) 2016-present, IBM Research * Licensed under The MIT License [see LICENSE for details] */ -export var LAMBDA: string; -export var MIMETYPE: string; +export const LAMBDA: "λ"; +export const MIMETYPE: "hk:mimeType"; diff --git a/dist/context.d.ts b/dist/context.d.ts index a66a098..9530077 100644 --- a/dist/context.d.ts +++ b/dist/context.d.ts @@ -10,8 +10,9 @@ declare class Context extends Node { * @param {string | null} [parent] Parent id. */ constructor(id?: string | null | undefined, parent?: string | null | undefined); + type: "context"; } declare namespace Context { - const type: string; + const type: "context"; } import Node = require("./node"); diff --git a/dist/datasource/hkdatasource.d.ts b/dist/datasource/hkdatasource.d.ts index 569a981..6068588 100644 --- a/dist/datasource/hkdatasource.d.ts +++ b/dist/datasource/hkdatasource.d.ts @@ -147,6 +147,18 @@ declare class HKDatasource { }, payload: object, callback: (err: string, entities: { [x: string]: HKEntity; }) => any): void; + /** + * @callback GetTrailCallback + * @param {string} err An error object that indicate if the operation was succesful or not + * @param {object} trail + */ + /** + * Fetch trail + * + * @param {string} trailId The trail id to retrieve their nested entities. + * @param {GetTrailCallback} callback Callback with the entities + */ + fetchTrail(trailId: string, callback?: (err: string, trail: object) => any): void; /** * Filter entities using CSS pattern `(TODO: document it better)` * diff --git a/dist/datasource/hkdatasource.js b/dist/datasource/hkdatasource.js index cf3e83c..28ed760 100644 --- a/dist/datasource/hkdatasource.js +++ b/dist/datasource/hkdatasource.js @@ -417,6 +417,40 @@ class HKDatasource { } }); } + /** + * @callback GetTrailCallback + * @param {string} err An error object that indicate if the operation was succesful or not + * @param {object} trail + */ + /** + * Fetch trail + * + * @param {string} trailId The trail id to retrieve their nested entities. + * @param {GetTrailCallback} callback Callback with the entities + */ + fetchTrail(trailId, callback = () => { }) { + let url = this.url + "repository/" + this.graphName + "/trail/" + encodeURIComponent(trailId); + request.get(url, this.options, (err, res) => { + // console.log(res.body); + if (!err) { + if (requestCompletedWithSuccess(res.statusCode)) { + try { + let entities = convertEntities(res.body); + callback(null, entities[trailId]); + } + catch (exp) { + callback(exp); + } + } + else { + callback(`Server responded with ${res.statusCode}. ${res.body}`); + } + } + else { + callback(err); + } + }); + } /** * Filter entities using CSS pattern `(TODO: document it better)` * diff --git a/dist/datasource/observer/clients/index.d.ts b/dist/datasource/observer/clients/index.d.ts index a830568..c47b151 100644 --- a/dist/datasource/observer/clients/index.d.ts +++ b/dist/datasource/observer/clients/index.d.ts @@ -2,7 +2,7 @@ * Copyright (c) 2016-present, IBM Research * Licensed under The MIT License [see LICENSE for details] */ -export var DefaultObserverClient: typeof import("./observerclient"); -export var ConfigurableObserverClient: typeof import("./configurableobserverclient"); -export var RestObserverClient: typeof import("./restobserverclient"); -export var RabbitMQObserverClient: typeof import("./rabbitmqobserverclient"); +export const DefaultObserverClient: typeof import("./observerclient"); +export const ConfigurableObserverClient: typeof import("./configurableobserverclient"); +export const RestObserverClient: typeof import("./restobserverclient"); +export const RabbitMQObserverClient: typeof import("./rabbitmqobserverclient"); diff --git a/dist/datasource/observer/clients/rabbitmqobserverclient.d.ts b/dist/datasource/observer/clients/rabbitmqobserverclient.d.ts index 4967183..cabd9a1 100644 --- a/dist/datasource/observer/clients/rabbitmqobserverclient.d.ts +++ b/dist/datasource/observer/clients/rabbitmqobserverclient.d.ts @@ -35,7 +35,9 @@ declare class RabbitMQObserverClient extends ConfigurableObserverClient { _channelWrapper: any; _setupFunction: ((channel: any) => Promise) | null; _queueName: any; + init(): Promise; _init(exchangeName: any): Promise; _isInitialized: boolean | undefined; + deinit(): Promise; } import ConfigurableObserverClient = require("./configurableobserverclient"); diff --git a/dist/datasource/observer/clients/restobserverclient.d.ts b/dist/datasource/observer/clients/restobserverclient.d.ts index 320c80d..5f9e354 100644 --- a/dist/datasource/observer/clients/restobserverclient.d.ts +++ b/dist/datasource/observer/clients/restobserverclient.d.ts @@ -25,5 +25,6 @@ declare class RestObserverClient extends ConfigurableObserverClient { _address: string; _listeningPath: string | null; _server: any; + deinit(): Promise; } import ConfigurableObserverClient = require("./configurableobserverclient"); diff --git a/dist/fi/fioperator.d.ts b/dist/fi/fioperator.d.ts index 6b04ff8..bb996e0 100644 --- a/dist/fi/fioperator.d.ts +++ b/dist/fi/fioperator.d.ts @@ -4,6 +4,6 @@ */ export = FIOperator; declare const FIOperator: Readonly<{ - NONE: string; - DESCRIPTION: string; + NONE: ""; + DESCRIPTION: "*"; }>; diff --git a/dist/hkgraph.d.ts b/dist/hkgraph.d.ts index d9de5ab..e6ebf8c 100644 --- a/dist/hkgraph.d.ts +++ b/dist/hkgraph.d.ts @@ -13,6 +13,7 @@ declare class HKGraph { connectors: {}; refs: {}; trails: {}; + actions: {}; bindsMap: {}; linkMap: {}; virtualLinkMap: {}; @@ -75,14 +76,14 @@ declare class HKGraph { deserialize(str: any): HKGraph; } declare namespace HKGraph { - const NODE_TYPE: string; - const VIRTUAL_NODE_TYPE: string; - const CONTEXT_TYPE: string; - const VIRTUAL_CONTEXT_TYPE: string; - const LINK_TYPE: string; - const VIRTUAL_LINK_TYPE: string; - const CONNECTOR_TYPE: string; - const INTERFACE: string; + const NODE_TYPE: "node"; + const VIRTUAL_NODE_TYPE: "virtualnode"; + const CONTEXT_TYPE: "context"; + const VIRTUAL_CONTEXT_TYPE: "virtualcontext"; + const LINK_TYPE: "link"; + const VIRTUAL_LINK_TYPE: "virtuallink"; + const CONNECTOR_TYPE: "connector"; + const INTERFACE: "interface"; } declare function generateId(model: any, length: any): any; import HKEntity = require("./hkentity"); diff --git a/dist/hkgraph.js b/dist/hkgraph.js index 52c1772..82f5a9a 100644 --- a/dist/hkgraph.js +++ b/dist/hkgraph.js @@ -11,6 +11,7 @@ const Reference = require("./reference"); const Trail = require("./trail"); const HKTypes = require("./types"); const shortId = require('shortid'); +const { Action } = require("./trail"); const VirtualContext = require("./virtualcontext"); const HKEntity = require("./hkentity"); const VirtualNode = require("./virtualnode"); @@ -26,6 +27,7 @@ class HKGraph { this.connectors = {}; this.refs = {}; this.trails = {}; + this.actions = {}; // Auxiliar maps this.bindsMap = {}; this.linkMap = {}; @@ -47,7 +49,8 @@ class HKGraph { this.links.hasOwnProperty(id) || this.connectors.hasOwnProperty(id) || this.refs.hasOwnProperty(id) || - this.trails.hasOwnProperty(id); + this.trails.hasOwnProperty(id) || + this.actions.hasOwnProperty(id); } /** * Update an entity @@ -66,7 +69,7 @@ class HKGraph { oldEntity.roles = entity.roles; oldEntity.className = entity.className; } - if (entity.type === HKTypes.NODE || entity.type === HKTypes.REFERENCE || entity.type === HKTypes.CONTEXT || entity.type === HKTypes.VIRTUAL_NODE || entity.type === HKTypes.VIRTUAL_CONTEXT) { + if (entity.type === HKTypes.NODE || entity.type === HKTypes.TRAIL || entity.type === HKTypes.REFERENCE || entity.type === HKTypes.CONTEXT || entity.type === HKTypes.VIRTUAL_NODE || entity.type === HKTypes.VIRTUAL_CONTEXT || entity.type === HKTypes.ACTION) { oldEntity.interfaces = entity.interfaces; } // Update parent @@ -173,6 +176,19 @@ class HKGraph { if (Trail.isValid(entity)) { newEntity = new Trail(entity); this.trails[entity.id] = newEntity; + this.contextMap[entity.id] = {}; + if (this.orphans.hasOwnProperty(entity.id)) { + this.contextMap[entity.id] = this.orphans[entity.id]; + delete this.orphans[entity.id]; + } + } + break; + } + case HKTypes.ACTION: + { + if (entity instanceof Trail.Action) { + newEntity = entity; + this.actions[entity.id] = newEntity; } break; } @@ -356,8 +372,13 @@ class HKGraph { { /* delete children trails? */ delete this.trails[id]; + delete this.contextMap[entity.id]; break; } + case Action.type: + { + delete this.actions[id]; + } } if (this.orphans.hasOwnProperty(entity.parent)) { delete this.orphans[entity.parent][id]; @@ -480,7 +501,7 @@ class HKGraph { c.id = null; return c; } - return this.nodes[id] || this.virtualNodes[id] || this.contexts[id] || this.virtualContexts[id] || this.virtualLinks[id] || this.links[id] || this.connectors[id] || this.refs[id] || this.trails[id] || null; + return this.nodes[id] || this.virtualNodes[id] || this.contexts[id] || this.virtualContexts[id] || this.virtualLinks[id] || this.links[id] || this.connectors[id] || this.refs[id] || this.trails[id] || this.actions[id] || null; } /** * Returns HK entities in this graph indexed by id. diff --git a/dist/index.js b/dist/index.js index 188c9cd..3bb9002 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32,6 +32,7 @@ exports.CONNECTOR_TYPE = require("./types").CONNECTOR; exports.BIND_TYPE = require("./types").BIND; exports.INTERFACE = require("./types").INTERFACE; exports.TRAIL_TYPE = require("./types").TRAIL; +exports.ACTION_TYPE = require("./types").ACTION; exports.VIRTUAL_NODE_TYPE = require("./types").VIRTUAL_NODE; exports.VIRTUAL_CONTEXT_TYPE = require("./types").VIRTUAL_CONTEXT; exports.VIRTUAL_LINK_TYPE = require("./types").VIRTUAL_LINK; diff --git a/dist/link.d.ts b/dist/link.d.ts index df42d86..a68f7c6 100644 --- a/dist/link.d.ts +++ b/dist/link.d.ts @@ -54,6 +54,8 @@ declare class Link extends HKEntity { [x: string]: Object; }; }; + properties: any; + metaProperties: any; /** * Adds a new bind to this role; * @@ -91,6 +93,6 @@ declare class Link extends HKEntity { }; } declare namespace Link { - const type: string; + const type: "link"; } import HKEntity = require("./hkentity"); diff --git a/dist/node.d.ts b/dist/node.d.ts index 4547689..6711716 100644 --- a/dist/node.d.ts +++ b/dist/node.d.ts @@ -60,6 +60,14 @@ declare class Node extends HKEntity { }; }; }; + /** + * @public + * @type {Object. + */ + public properties: { + [x: string]: string | number | Object; + }; + metaProperties: any; /** * * @param {string} key Id of the interface @@ -88,7 +96,7 @@ declare class Node extends HKEntity { }; } declare namespace Node { - const type: string; + const type: "node"; } import HKEntity = require("./hkentity"); import FI = require("./fi/fi"); diff --git a/dist/reference.d.ts b/dist/reference.d.ts index 9656433..152372c 100644 --- a/dist/reference.d.ts +++ b/dist/reference.d.ts @@ -20,8 +20,15 @@ declare class Reference extends Node { * */ public ref: string | null; + type: "ref"; + serialize: () => { + id: any; + type: "ref"; + ref: any; + parent: any; + }; } declare namespace Reference { - const type: string; + const type: "ref"; } import Node = require("./node"); diff --git a/dist/roletypes.d.ts b/dist/roletypes.d.ts index b1e3c10..cd83553 100644 --- a/dist/roletypes.d.ts +++ b/dist/roletypes.d.ts @@ -2,8 +2,8 @@ * Copyright (c) 2016-present, IBM Research * Licensed under The MIT License [see LICENSE for details] */ -export var NONE: string; -export var SUBJECT: string; -export var OBJECT: string; -export var PARENT: string; -export var CHILD: string; +export const NONE: "n"; +export const SUBJECT: "s"; +export const OBJECT: "o"; +export const PARENT: "p"; +export const CHILD: "c"; diff --git a/dist/trail.d.ts b/dist/trail.d.ts index 6bb2f2a..948a90f 100644 --- a/dist/trail.d.ts +++ b/dist/trail.d.ts @@ -3,29 +3,83 @@ * Licensed under The MIT License [see LICENSE for details] */ export = Trail; -declare class Trail extends HKEntity { - static isValid(entity: any): boolean; - constructor(id: any, parent: any, ...args: any[]); +declare function Trail(id: any, actions: any, parent: any, ...args: any[]): void; +declare class Trail { + constructor(id: any, actions: any, parent: any, ...args: any[]); id: any; parent: any; + properties: any; metaproperties: any; interfaces: any; - children: any; - steps: any[] | undefined; - addStep(key: any, properties: any): void; - addInterface(key: any, type: any, properties: any): void; - createLinksFromSteps(): Connector[]; + actions: any; + type: "trail"; + updateAction(oldAction: any, newAction: any): void; + addAction(action: any): any; + removeAction(action: any): void; + action: any; + append(action: any): any; + prepend(action: any): any; + in(from?: null): any; + out(to?: null): any; + size(): number | undefined; + join(delimiter: any): any; + update(action: any, newAction: any): void; + remove(action: any): void; + getPrev(position: any, num?: number): any; + getNext(position: any, num?: number): any; + getPositionOf(action: any): number; + getActionAt(position: any): any; + search(eventId: null | undefined, filters: any): any; + loadActions(actions?: null): any; + toJSON(): { + id: any; + parent: any; + properties: any; + metaproperties: any; + interfaces: any; + actions: any[]; + type: "trail"; + }; serialize(): { id: any; parent: any; properties: any; metaproperties: any; interfaces: any; - type: string; + actions: any; + type: "trail"; }; } declare namespace Trail { - const type: string; + export const type: "trail"; + export { isValid }; + export { sort }; + export { Action }; + export { TrailNode }; +} +declare function isValid(entity: any): boolean; +declare function sort(actions?: null): any; +declare class Action { + constructor({ from, to, event, agent }?: { + from?: null | undefined; + to?: null | undefined; + event?: {} | undefined; + agent?: null | undefined; + }); + from: any; + to: any; + agent: any; + event: {}; + type: "action"; + id: any; + getTime(): number; + toString(): any; + toJSON(): any; +} +declare class TrailNode { + constructor(nodeId: any, nodeType: any, targetAnchor: any); + nodeId: any; + nodeType: any; + targetAnchor: any; + toString(): string; } -import HKEntity = require("./hkentity"); -import Connector = require("./connector"); diff --git a/dist/trail.js b/dist/trail.js index 2c1186c..2e2235d 100644 --- a/dist/trail.js +++ b/dist/trail.js @@ -1,113 +1,408 @@ -/** +/* * Copyright (c) 2016-present, IBM Research * Licensed under The MIT License [see LICENSE for details] */ 'use strict'; -const shortid = require('shortid'); +const { List, Item } = require('linked-list'); +// const shortid = require('shortid'); const Types = require('./types'); const HKEntity = require('./hkentity'); -const Connector = require('./connector'); -const Link = require('./link'); -const RoleTypes = require('./roletypes'); -const CONNECTOR_NAME = 'occurs'; -const vConnector = new Connector(CONNECTOR_NAME, 'f'); -vConnector.addRole('sub', RoleTypes.SUBJECT); -vConnector.addRole('obj', RoleTypes.OBJECT); -class Trail extends HKEntity { - constructor(id, parent) { - super(); - if (arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) { - let trail = arguments[0]; - this.id = trail.id || null; - this.parent = trail.parent || null; - this.properties = trail.properties || {}; - this.metaproperties = trail.metaproperties || {}; - this.interfaces = trail.interfaces || {}; - this.children = trail.children || []; - _loadSteps.call(this); +function Trail(id, actions, parent) { + if (arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) { + let trail = arguments[0]; + this.id = trail.id || null; + this.parent = trail.parent || null; + this.properties = trail.properties || {}; + this.metaproperties = trail.metaproperties || {}; + this.interfaces = trail.interfaces || {}; + this.actions = trail.actions || new List(); + if (this.actions && Object.keys(this.actions).length > 0) { + this.loadActions(); + } + } + else { + this.id = id || null; + this.parent = parent || null; + this.interfaces = {}; + this.properties = {}; + this.metaproperties = {}; + this.actions = actions || new List(); + } + this.type = Types.TRAIL; +} +Trail.prototype = Object.create(HKEntity.prototype); +//Trail.prototype.constructor = Trail; +// Trail.prototype = Object.assign(Trail.prototype, List.prototype); // Multiple inheritance with assign +// Update a given action in trail +Trail.prototype.updateAction = function (oldAction, newAction) { + oldAction.prepend(newAction); + oldAction.detach(); +}; +// Add an action to a trail respecting timestamp ordering +Trail.prototype.addAction = function (action) { + var current = this.actions.head; + while (current) { + if (new Date(current.event['timestamp']) - new Date(action.event['timestamp'])) { + return current.prepend(action); + } + current = current.next; + } + return this.actions.append(action); +}; +// Remove an action from trail using its reference +Trail.prototype.removeAction = function (action) { + var action; + if (typeof (action) === 'string') { + this.action = this.search(action); + //action not found + if (!this.action) { + return; + } + } + else { + this.action = action; + } + this.action.detach(); +}; +// `append` and `prepend` operations are +// handled by our linked list structure +Trail.prototype.append = function (action) { + return this.actions.append(action); +}; +Trail.prototype.prepend = function (action) { + return this.actions.prepend(action); +}; +// aliases `in` and `out` represent the input and +// output TrailNodes for a trail, which conceptually is +// done through the use of Port elements in Hyperknowledge +Trail.prototype.in = function (from = null) { + if (from) { + this.actions.head.from = from; + } + return this.actions.head.from; +}; +Trail.prototype.out = function (to = null) { + if (this.actions.tail) { + if (to) { + this.actions.tail.to = to; + } + return this.actions.tail.to; + } + if (to) { + this.actions.head.to = to; + } + return this.actions.head.to; +}; +Trail.prototype.size = function () { + if (this.actions.constructor === List) { + return this.actions.size; + } + else if (Array.isArray(this.actions)) { + return this.actions.length; + } +}; +Trail.prototype.join = function (delimiter) { + return this.actions.toArray().join(delimiter); +}; +// Update a given action in the trail +Trail.prototype.update = function (action, newAction) { + action.prepend(newAction); + action.detach(); +}; +// Remove an action from the trail using its reference +Trail.prototype.remove = function (action) { + var action; + if (typeof (action) === 'string') { + this.action = this.search(action); + //action not found + if (!this.action) { + return; + } + } + else { + this.action = action; + } + this.action.detach(); +}; +// Get `num` actions (or the available ones) before `position` +Trail.prototype.getPrev = function (position, num = 1) { + //check if it is a valid position + if (position >= this.actions.size || position <= 1 || this.actions.size <= 1) { + return; + } + // get actions + var action = this.actions.toArray()[position]; + var resultSet = []; + while (action && action.prev) { + if (resultSet.length >= num) { + if (resultSet.length == 1 && num == 1) { + return resultSet[0]; + } + return resultSet; } else { - this.id = id || null; - this.parent = parent || null; - this.interfaces = {}; - this.properties = {}; - this.metaproperties = {}; - this.children = []; - this.steps = []; - } - this.type = Types.TRAIL; - } - addStep(key, properties) { - let ts = properties.begin || new Date().toISOString(); - properties.begin = ts; - if (this.steps.length > 0) { - let lastStep = this.steps[this.steps.length - 1].key; - if (!this.interfaces[lastStep].properties.end) { - this.interfaces[lastStep].properties.end = ts; + resultSet.push(action.prev); + } + action = action.prev; + } + return resultSet; +}; +// Get `num` actions (or a n= this.actions.size || position <= 1 || this.actions.size <= 1) { + return; + } + // get actions + var action = this.actions.toArray()[position]; + var resultSet = []; + while (action && action.next) { + if (resultSet.length >= num) { + if (resultSet.length == 1 && num == 1) { + return resultSet[0]; } } - this.steps.push({ key: key, begin: ts }); - this.addInterface(key, 'temporal', properties); - } - addInterface(key, type, properties) { - this.interfaces[key] = { type: type, properties: properties }; - } - createLinksFromSteps() { - let vEntities = [vConnector]; - for (let key in this.interfaces) { - let interProp = this.interfaces[key].properties; - if (!interProp) - continue; - if (interProp.obj) { - let l = new Link(shortid(), vConnector.id, this.parent); - l.addBind('sub', interProp.obj, interProp.objInterface); - l.addBind('obj', this.id); - vEntities.push(l); + else { + resultSet.push(action.next); + } + action = action.next; + } + return resultSet; +}; +// Get the position of an action in the list, +// it might be useful for pagination. Returns +// -1 in case action is not found in the list +Trail.prototype.getPositionOf = function (action) { + //search action by eventId + var array = this.actions.toArray(); + for (var i = 0; i < this.actions.size; i++) { + if (array[i].event["id"] == action.event["id"]) { + return i; + } + } + return -1; +}; +// Get an action at specified position +// a position = 0 is equivalent to trail.head +Trail.prototype.getActionAt = function (position) { + //check if it is a valid position + if (position < 0 || position >= this.actions.size) { + return; + } + //return action by position in array + return this.actions.toArray()[position]; +}; +// Search for action(s) either by an event identifier or by filters +Trail.prototype.search = function (eventId = null, filters) { + if (!filters && eventId instanceof String) { + filters = { 'from': null, 'fromAnchor': 'lambda', 'to': null, 'toAnchor': 'lambda' }; + } + else if (!filters && eventId instanceof Object) { + filters = eventId; + eventId = null; + } + // nothing to be found + if (!eventId && !filters['from'] && !filters['to']) { + return; + } + // search actions by eventId + var array = this.actions.toArray(); + if (eventId) { + for (var i = 0; i < this.actions.size; i++) { + if (array[i].event["id"] == eventId) { + return array[i]; } } - return vEntities; - } - serialize() { - return { - id: this.id, - parent: this.parent, - properties: this.properties, - metaproperties: this.metaproperties, - interfaces: this.interfaces, - type: this.type - }; - } - static isValid(entity) { - let isValid = false; - if (entity && typeof (entity) === 'object' && !Array.isArray(entity)) { - if (entity.hasOwnProperty('type') && entity.type === Types.TRAIL && - entity.hasOwnProperty('id') && entity.hasOwnProperty('parent')) { - isValid = true; + return; + } + // search actions by filters + var resultSet = []; + for (var i = 0; i < this.actions.size; i++) { + if ((filters['from'] && filters['to']) && array[i].from == filters['from'] && array[i].to == filters['to']) { + resultSet.push(array[i]); + } + else if (array[i].from == filters['from'] || array[i].to == filters['to']) { + resultSet.push(array[i]); + } + } + return resultSet; +}; +Trail.prototype.loadActions = function (actions = null) { + // use array of actions if passed + if (actions && Array.isArray(actions)) { + if (actions[0].event.timestamp) { + this.actions = new List(...sort(actions)); + } + else { + this.actions = new List(...actions); + } + return this.actions; + } + let actionArray = []; + let actionIds = []; + for (let i in this.actions) { + // create Action object from json object + if (this.actions[i] && this.actions[i].hasOwnProperty("from") && + this.actions[i].hasOwnProperty("to") && + this.actions[i].hasOwnProperty("agent") && + this.actions[i].hasOwnProperty("event")) { + let from = new TrailNode(this.actions[i].from.nodeId, this.actions[i].from.nodeType, this.actions[i].from.targetAnchor); + let to = new TrailNode(this.actions[i].to.nodeId, this.actions[i].to.nodeType, this.actions[i].to.targetAnchor); + let event = { "id": this.actions[i].event.id, "type": this.actions[i].event.type, "properties": this.actions[i].event.properties, "timestamp": new Date(this.actions[i].event.timestamp) }; + let agent = this.actions[i].agent; + actionArray.push(new Action({ from, to, event, agent })); + } + else if (this.actions[i] && this.actions[i].hasOwnProperty("event") && this.actions[i].event.hasOwnProperty("timestamp")) { + // we got event's id and timestamp + actionArray.push(new Action({ event: { "id": i, "timestamp": new Date(this.actions[i].event.timestamp) } })); + } + else if (Array.isArray(this.actions)) { + // all we got is an Array with the event's id + actionIds.push(this.actions[i]); + } + else { + // all we got is event's id + actionIds.push(i); + } + } + // sort action items before creating list + if (actionArray.length > 0) { + this.actions = new List(...sort(actionArray)); + } + else if (actionIds.length > 0) { + this.actions = actionIds; + } +}; +function isValid(entity) { + let isValid = false; + if (entity && typeof (entity) === 'object' && !Array.isArray(entity)) { + if (entity.hasOwnProperty('type') && entity.type === Types.TRAIL && + entity.hasOwnProperty('id') && entity.hasOwnProperty('parent')) { + isValid = true; + } + } + return isValid; +} +function sort(actions = null) { + if (actions && Array.isArray(actions) && actions[0].event.timestamp.constructor === Date) { + // sort and return object array based on timestamp + return actions.sort(function (action1, action2) { + return action1.event['timestamp'] - action2.event['timestamp']; + }); + } + else if (actions && Array.isArray(actions) && (actions[0].event.timestamp.constructor === String || actions[0].event.timestamp.constructor === Number)) { + // sort Action array based on timestamp + return actions.sort(function (action1, action2) { + return new Date(action1.event['timestamp']) - new Date(action2.event['timestamp']); + }); + } +} +Trail.prototype.toJSON = function () { + let actionArray = []; + if (this.actions.constructor === List) { + //in case we have only action ids and timestamps + if (this.actions.head && (!this.actions.head.from && !this.actions.head.to && !this.actions.head.agent)) { + let action = this.actions.head; + while (action) { + actionArray.push(action.id); + action = action.next; } } - return isValid; + else { + actionArray = this.actions.toArray(); + } + } + else if (Array.isArray(this.actions)) { + actionArray = this.actions; + } + return { + id: this.id, + parent: this.parent, + properties: this.properties, + metaproperties: this.metaproperties, + interfaces: this.interfaces, + actions: actionArray, + type: this.type + }; +}; +Trail.prototype.serialize = function () { + let actions; + if (this.actions instanceof Array) { + actions = this.actions; + } + else { + actions = this.actions.toArray(); + } + return { + id: this.id, + parent: this.parent, + properties: this.properties, + metaproperties: this.metaproperties, + interfaces: this.interfaces, + actions: actions, + type: this.type + }; +}; +// TrailNode is basically an envelope for a hknode and a target anchor +class TrailNode { + constructor(nodeId, nodeType, targetAnchor) { + this.nodeId = nodeId; + this.nodeType = nodeType; + this.targetAnchor = targetAnchor; + } + toString() { + return this.nodeId + "#" + this.targetAnchor; } } -function _loadSteps() { - let steps = []; - for (let key in this.interfaces) { - let begin = new Date(Date.parse(this.interfaces[key].properties.begin)); - let end = new Date(Date.parse(this.interfaces[key].properties.end)); - this.interfaces[key].properties.begin = begin; - this.interfaces[key].properties.end = end; - steps.push({ key: key, begin: begin }); - } - steps.sort((a, b) => { - if (a.begin < b.begin) { - return -1; - } - if (a.begin > b.begin) { - return 1; - } - return 0; - }); - this.steps = steps; +// actions hold references to source and destination trail +// nodes (along with their respective target anchors), +// the related event and an agent (user, system or content). +class Action extends Item { + constructor({ from = null, to = null, event = {}, agent = null } = {}) { + super(); + this.from = from; + this.to = to; + this.agent = agent; + this.event = event; + this.type = Types.ACTION; + // create timestamp if needed + // if(!this.event['timestamp']) + // { + // this.event['timestamp'] = new Date().getTime(); + // } + // get event id or create a new one + if (!this.event['id'] || this.event['id'] == '') { + this.event['id'] = this.event.type + '_' + this.event.timestamp; + this.id = this.event['id']; + } + else { + this.id = this.event['id']; + } + } + getTime() { + return new Date(this.event['timestamp']).getTime(); + } + toString() { + if (this.from && this.to && this.agent && this.event && this.event.id) { + return JSON.stringify({ from: this.from, to: this.to, agent: this.agent, event: this.event, id: this.event.id }); + } + else { + return this.event.id; + } + } + toJSON() { + if (this.from && this.to && this.agent && this.event && this.event.id) { + return { from: this.from, to: this.to, agent: this.agent, event: this.event, id: this.event.id }; + } + else { + return this.event.id; + } + } } Trail.type = Types.TRAIL; -const isValid = Trail.isValid; +Trail.isValid = isValid; +Trail.sort = sort; +Trail.Action = Action; +Trail.TrailNode = TrailNode; module.exports = Trail; diff --git a/dist/types.d.ts b/dist/types.d.ts index a59c438..2948170 100644 --- a/dist/types.d.ts +++ b/dist/types.d.ts @@ -2,16 +2,17 @@ * Copyright (c) 2016-present, IBM Research * Licensed under The MIT License [see LICENSE for details] */ -export var NODE: string; -export var CONTEXT: string; -export var LINK: string; -export var CONNECTOR: string; -export var REFERENCE: string; -export var INTERFACE: string; -export var BIND: string; -export var TRAIL: string; -export var VIRTUAL_NODE: string; -export var VIRTUAL_CONTEXT: string; -export var VIRTUAL_LINK: string; -export var VIRTUAL_SOURCE_PROPERTY: string; +export const NODE: "node"; +export const CONTEXT: "context"; +export const LINK: "link"; +export const CONNECTOR: "connector"; +export const REFERENCE: "ref"; +export const INTERFACE: "interface"; +export const BIND: "bind"; +export const TRAIL: "trail"; +export const ACTION: "action"; +export const VIRTUAL_NODE: "virtualnode"; +export const VIRTUAL_CONTEXT: "virtualcontext"; +export const VIRTUAL_LINK: "virtuallink"; +export const VIRTUAL_SOURCE_PROPERTY: "virtualsrc"; export function isValidType(type: any): boolean; diff --git a/dist/types.js b/dist/types.js index 46f60eb..fff6183 100644 --- a/dist/types.js +++ b/dist/types.js @@ -11,6 +11,7 @@ exports.REFERENCE = 'ref'; exports.INTERFACE = 'interface'; exports.BIND = 'bind'; exports.TRAIL = 'trail'; +exports.ACTION = 'action'; exports.VIRTUAL_NODE = 'virtualnode'; exports.VIRTUAL_CONTEXT = 'virtualcontext'; exports.VIRTUAL_LINK = 'virtuallink'; diff --git a/dist/virtualcontext.js b/dist/virtualcontext.js index a9e4ac0..5d7bf58 100644 --- a/dist/virtualcontext.js +++ b/dist/virtualcontext.js @@ -5,7 +5,7 @@ */ const tslib_1 = require("tslib"); const types_1 = require("./types"); -const context_1 = (0, tslib_1.__importDefault)(require("./context")); +const context_1 = tslib_1.__importDefault(require("./context")); class VirtualContext extends context_1.default { /** Constructs a new virtual context object. * diff --git a/dist/virtuallink.js b/dist/virtuallink.js index dc4d63a..ccd6c5d 100644 --- a/dist/virtuallink.js +++ b/dist/virtuallink.js @@ -5,7 +5,7 @@ */ const tslib_1 = require("tslib"); const types_1 = require("./types"); -const link_1 = (0, tslib_1.__importDefault)(require("./link")); +const link_1 = tslib_1.__importDefault(require("./link")); class VirtualLink extends link_1.default { constructor(id, parent) { super(id, parent); diff --git a/dist/virtualnode.js b/dist/virtualnode.js index d91b4f9..1205bae 100644 --- a/dist/virtualnode.js +++ b/dist/virtualnode.js @@ -5,7 +5,7 @@ */ const tslib_1 = require("tslib"); const types_1 = require("./types"); -const node_1 = (0, tslib_1.__importDefault)(require("./node")); +const node_1 = tslib_1.__importDefault(require("./node")); class VirtualNode extends node_1.default { constructor(id, parent) { super(id, parent); From adf93a061d4b4a9e99fa01d95cdbea798276acfc Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Fri, 1 Jul 2022 14:51:24 -0300 Subject: [PATCH 22/24] undo mend --- dist/trail.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/trail.d.ts b/dist/trail.d.ts index 6bb2f2a..1573ca0 100644 --- a/dist/trail.d.ts +++ b/dist/trail.d.ts @@ -29,3 +29,4 @@ declare namespace Trail { } import HKEntity = require("./hkentity"); import Connector = require("./connector"); + From a43ca6da12a0078071f7fe8a441bbd057dd8db91 Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Mon, 11 Jul 2022 16:51:56 -0300 Subject: [PATCH 23/24] Updated trail from function to class --- src/trail.js | 963 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 595 insertions(+), 368 deletions(-) diff --git a/src/trail.js b/src/trail.js index 126f96a..969d739 100644 --- a/src/trail.js +++ b/src/trail.js @@ -6,462 +6,670 @@ 'use strict' const {List, Item} = require('linked-list'); - -// const shortid = require('shortid'); const Types = require('./types'); const HKEntity = require('./hkentity'); - -function Trail (id, actions, parent) +class Trail extends HKEntity { - - if(arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) - { - let trail = arguments[0]; - - this.id = trail.id || null; - this.parent = trail.parent || null; - this.properties = trail.properties || {}; - this.metaproperties = trail.metaproperties || {}; - this.interfaces = trail.interfaces || {}; - this.actions = trail.actions || new List(); - - if (this.actions && Object.keys(this.actions).length > 0) { - this.loadActions(); - } - } - else + /** + * Constructs a new trail object. + * + * @param {string | null} [id] Some id string for this trail. Deprecated: json object, which will deserialized as a Trail. + * @param {List | Array | null} [actions] Trail actions. + * @param {string | null} [parent] Parent id. + */ + + constructor(id = null, actions = null, parent = null) { - this.id = id || null; - this.parent = parent || null; - this.interfaces = {}; - this.properties = {}; - this.metaproperties = {}; + super(); + + /** + * + * Id of this node. Might be null. + * + * @public + * @type {string | null} + * + */ + this.id = id; + + /** + * Trail actions. Might be null. + * + * @public + * @type {List | Array | null} + * + */ this.actions = actions || new List(); - } + /** + * Parent id. Might be null. + * + * @public + * @type {string | null} + * + */ + this.parent = parent; + + /** + * Type of this node. + * + * @public + * @type {string | null} + */ + this.type = Types.TRAIL; + + /** + * Interface attributed to this node. + * + * @public + * @type {Object.}>} + */ + this.interfaces = {}; - this.type = Types.TRAIL; -} + /** + * @public + * @type {Object. + */ + this.properties = {}; -Trail.prototype = Object.create (HKEntity.prototype); -// Trail.prototype = Object.assign(Trail.prototype, List.prototype); // Multiple inheritance with assign + /** + * @public + * @type {Object. + */ + this.metaproperties = {}; + // TODO: this code seems to copy a trail passed as an id. Create a separate clone/json-deserialize function for that. + if(arguments[0] && typeof arguments[0] === 'object' && isValid(arguments[0])) + { + let trail = arguments[0]; -// Update a given action in trail -Trail.prototype.updateAction = function (oldAction, newAction) -{ - oldAction.prepend(newAction); - oldAction.detach(); -} + this.id = trail.id || null; + this.parent = trail.parent || null; + this.properties = trail.properties || {}; + this.metaproperties = trail.metaproperties || {}; + this.interfaces = trail.interfaces || {}; + this.actions = trail.actions || new List(); -// Add an action to a trail respecting timestamp ordering -Trail.prototype.addAction = function (action) -{ - var current = this.actions.head; + if (this.actions && Object.keys(this.actions).length > 0) { + this.loadActions(); + } + } + } + + /** + * Update a given action in trail. + * + * @param {Trail.Action} oldAction + * @param {Trail.Action} newAction + */ + updateAction(oldAction, newAction) + { + oldAction.prepend(newAction); + oldAction.detach(); + } - while (current) + /** + * Add an action to a trail respecting timestamp ordering. + * + * @param {Trail.Action} action + * @returns {Trail.Action} + */ + addAction(action) { - if (new Date(current.event['timestamp']) - new Date(action.event['timestamp'])) + var current = this.actions.head; + + while (current) { - return current.prepend(action); + if (new Date(current.event['timestamp']) - new Date(action.event['timestamp'])) + { + return current.prepend(action); + } + current = current.next; } - current = current.next; - } - return this.actions.append(action); -} + return this.actions.append(action); + } -// Remove an action from trail using its reference -Trail.prototype.removeAction = function (action) -{ - var action; - if(typeof(action) === 'string') { - this.action = this.search(action) - - //action not found - if(!this.action){ - return - } - } - else { - this.action = action; - } + /** + * Remove an action from trail using its reference. + * + * @param {Trail.Action} action + * @returns {Trail.Action} + */ + removeAction(action) + { + var action; + if(typeof(action) === 'string') { + this.action = this.search(action) + + //action not found + if(!this.action){ + return + } + } + else { + this.action = action; + } - this.action.detach(); -} + this.action.detach(); + } -// `append` and `prepend` operations are -// handled by our linked list structure -Trail.prototype.append = function(action) -{ - return this.actions.append(action); -} + /** + * `append` and `prepend` operations are + * handled by our linked list structure. + * + * @param {Trail.Action} action + * @returns {Trail.Action} + */ + append(action) + { + return this.actions.append(action); + } -Trail.prototype.prepend = function(action) -{ - return this.actions.prepend(action); -} + /** + * + * @param {Trail.Action} action + * @returns {Trail.Action} + */ + prepend(action) + { + return this.actions.prepend(action); + } -// aliases `in` and `out` represent the input and -// output TrailNodes for a trail, which conceptually is -// done through the use of Port elements in Hyperknowledge -Trail.prototype.in = function(from = null) { - if (from) + /** + * Aliases `in` and `out` represent the input and + * output TrailNodes for a trail, which conceptually is + * done through the use of Port elements in Hyperknowledge. + * + * @param {Trail.Action | null} from + * @returns {Trail.Action} + */ + in(from = null) { - this.actions.head.from = from; + if (from) + { + this.actions.head.from = from; + } + + return this.actions.head.from; } - - return this.actions.head.from; -} -Trail.prototype.out = function(to = null) { - if(this.actions.tail) + /** + * + * @param {Trail.Action | null} to + * @returns {Trail.Action} + */ + out(to = null) { + if(this.actions.tail) + { + if (to) + { + this.actions.tail.to = to; + } + + return this.actions.tail.to; + } + if (to) { - this.actions.tail.to = to; + this.actions.head.to = to; } - return this.actions.tail.to; + return this.actions.head.to; } - if (to) + /** + * + * @returns {number} + */ + size() { - this.actions.head.to = to; + if (this.actions.constructor === List) + { + return this.actions.size; + } + else if (Array.isArray(this.actions)) + { + return this.actions.length; + } } - - return this.actions.head.to; -} -Trail.prototype.size = function() -{ - if (this.actions.constructor === List) + /** + * + * @param {string} delimiter + * @returns {string} + */ + join(delimiter) { - return this.actions.size; + return this.actions.toArray().join(delimiter) } - else if (Array.isArray(this.actions)) + + /** + * Update a given action in the trail. + * + * @param {Trail.Action} action + * @param {Trail.Action} newAction + */ + // + update(action, newAction) { - return this.actions.length; + action.prepend(newAction) + action.detach() } -} -Trail.prototype.join = function(delimiter) -{ - return this.actions.toArray().join(delimiter) -} - -// Update a given action in the trail -Trail.prototype.update = function(action, newAction) -{ - action.prepend(newAction) - action.detach() -} - -// Remove an action from the trail using its reference -Trail.prototype.remove = function(action) -{ - var action - if(typeof(action) === 'string') { - this.action = this.search(action) - - //action not found - if(!this.action){ - return - } - } - else { - this.action = action - } - - this.action.detach() -} + /** + * Remove an action from the trail using its reference. + * + * @param {Trail.Action | string} action + * @returns {Trail.Action | null} + */ + // + remove(action) + { + var action + if(typeof(action) === 'string') { + this.action = this.search(action) + + //action not found + if(!this.action){ + return + } + } + else { + this.action = action + } -// Get `num` actions (or the available ones) before `position` -Trail.prototype.getPrev = function(position, num=1){ - //check if it is a valid position - if (position >= this.actions.size || position <= 1 || this.actions.size <= 1){ - return; - } + this.action.detach() + } - // get actions - var action = this.actions.toArray()[position] - var resultSet = [] - while(action && action.prev){ - if (resultSet.length >= num) { - if(resultSet.length == 1 && num == 1){ - return resultSet[0] - } - return resultSet - } - else { - resultSet.push(action.prev) - } - - action = action.prev - } + /** + * Get `num` actions (or the available ones) before `position`. + * + * If a number is not given, 1 is assumed. + * + * @param {number} position + * @param {number | null} num + * @returns {Array | null} + */ + getPrev = function(position, num=1) + { + //check if it is a valid position + if (position >= this.actions.size || position <= 1 || this.actions.size <= 1){ + return; + } - return resultSet -} + // get actions + var action = this.actions.toArray()[position] + var resultSet = [] + while(action && action.prev){ + if (resultSet.length >= num) { + if(resultSet.length == 1 && num == 1){ + return resultSet[0] + } + return resultSet + } + else { + resultSet.push(action.prev) + } + + action = action.prev + } -// Get `num` actions (or a n= this.actions.size || position <= 1 || this.actions.size <= 1){ - return; - } + return resultSet + } - // get actions - var action = this.actions.toArray()[position] - var resultSet = [] - while(action && action.next){ - if (resultSet.length >= num) { - if(resultSet.length == 1 && num == 1){ - return resultSet[0] - } - } - else { - resultSet.push(action.next) - } - - action = action.next - } + /** + * Get `num` actions (or a n | null} + */ + getNext(position, num=1) + { + // check if it is a valid position + if (position >= this.actions.size || position <= 1 || this.actions.size <= 1){ + return; + } - return resultSet -} + // get actions + var action = this.actions.toArray()[position] + var resultSet = [] + while(action && action.next){ + if (resultSet.length >= num) { + if(resultSet.length == 1 && num == 1){ + return resultSet[0] + } + } + else { + resultSet.push(action.next) + } + + action = action.next + } -// Get the position of an action in the list, -// it might be useful for pagination. Returns -// -1 in case action is not found in the list -Trail.prototype.getPositionOf = function(action){ - //search action by eventId - var array = this.actions.toArray(); - for (var i = 0; i < this.actions.size; i++) { - if(array[i].event["id"] == action.event["id"]){ - return i; - } - } - return -1; -} + return resultSet + } -// Get an action at specified position -// a position = 0 is equivalent to trail.head -Trail.prototype.getActionAt = function(position) { - //check if it is a valid position - if (position < 0 || position >= this.actions.size ){ - return; - } + /** + * Get the position of an action in the list, + * it might be useful for pagination. + * + * Returns -1 in case action is not found in the list. + * + * @param {Trail.Action} action + * @returns {number} + */ + + getPositionOf(action) + { + //search action by eventId + var array = this.actions.toArray(); + for (var i = 0; i < this.actions.size; i++) { + if(array[i].event["id"] == action.event["id"]){ + return i; + } + } + return -1; + } - //return action by position in array - return this.actions.toArray()[position] -} + /** + * Get an action at specified position. + * + * Position = 0 is equivalent to trail.head. + * + * @param {number} position + * @returns {Trail.Action} + */ + getActionAt(position) + { + //check if it is a valid position + if (position < 0 || position >= this.actions.size ){ + return; + } -// Search for action(s) either by an event identifier or by filters -Trail.prototype.search = function(eventId = null, filters){ - if (!filters && eventId instanceof String) { - filters = {'from': null, 'fromAnchor': 'lambda', 'to': null, 'toAnchor': 'lambda'} - } - else if (!filters && eventId instanceof Object){ - filters = eventId - eventId = null - } - - // nothing to be found - if (!eventId && !filters['from'] && !filters['to']){ - return - } + //return action by position in array + return this.actions.toArray()[position] + } - // search actions by eventId - var array = this.actions.toArray(); - if (eventId){ - for (var i = 0; i < this.actions.size; i++) { - if(array[i].event["id"] == eventId){ - return array[i]; - } - } - return; - } + /** + * Search for action(s) either by an event identifier or by filters. + * + * @param {Object | string | null} eventId + * @param {Object} filters + * @return {Array | null} + */ + search(eventId = null, filters) + { + if (!filters && eventId instanceof String) { + filters = {'from': null, 'fromAnchor': 'lambda', 'to': null, 'toAnchor': 'lambda'} + } + else if (!filters && eventId instanceof Object){ + filters = eventId + eventId = null + } + + // nothing to be found + if (!eventId && !filters['from'] && !filters['to']){ + return + } - // search actions by filters - var resultSet = [] - for (var i = 0; i < this.actions.size; i++) { - if((filters['from'] && filters['to']) && array[i].from == filters['from'] && array[i].to == filters['to']){ - resultSet.push(array[i]) - } - else if(array[i].from == filters['from'] || array[i].to == filters['to']){ - resultSet.push(array[i]) - } - } - return resultSet -} + // search actions by eventId + var array = this.actions.toArray(); + if (eventId){ + for (var i = 0; i < this.actions.size; i++) { + if(array[i].event["id"] == eventId){ + return array[i]; + } + } + return; + } -Trail.prototype.loadActions = function (actions = null) -{ + // search actions by filters + var resultSet = [] + for (var i = 0; i < this.actions.size; i++) { + if((filters['from'] && filters['to']) && array[i].from == filters['from'] && array[i].to == filters['to']){ + resultSet.push(array[i]) + } + else if(array[i].from == filters['from'] || array[i].to == filters['to']){ + resultSet.push(array[i]) + } + } + return resultSet + } - // use array of actions if passed - if(actions && Array.isArray(actions)) + /** + * + * @param {Array | null} actions + * @return {List | null} + */ + loadActions(actions = null) { - if(actions[0].event.timestamp) + + // use array of actions if passed + if(actions && Array.isArray(actions)) { - this.actions = new List(...sort(actions)); - } - else { - this.actions = new List(...actions); + if(actions[0].event.timestamp) + { + this.actions = new List(...sort(actions)); + } + else { + this.actions = new List(...actions); + } + return this.actions; } - return this.actions; - } - let actionArray = [] - let actionIds = [] - for (let i in this.actions) - { - // create Action object from json object - if (this.actions[i] && this.actions[i].hasOwnProperty("from") && - this.actions[i].hasOwnProperty("to") && - this.actions[i].hasOwnProperty("agent") && - this.actions[i].hasOwnProperty("event")) + let actionArray = [] + let actionIds = [] + for (let i in this.actions) { - let from = new TrailNode(this.actions[i].from.nodeId, this.actions[i].from.nodeType, this.actions[i].from.targetAnchor); - let to = new TrailNode(this.actions[i].to.nodeId, this.actions[i].to.nodeType, this.actions[i].to.targetAnchor); - let event = { "id": this.actions[i].event.id, "type": this.actions[i].event.type, "properties": this.actions[i].event.properties, "timestamp": new Date(this.actions[i].event.timestamp)}; - let agent = this.actions[i].agent; + // create Action object from json object + if (this.actions[i] && this.actions[i].hasOwnProperty("from") && + this.actions[i].hasOwnProperty("to") && + this.actions[i].hasOwnProperty("agent") && + this.actions[i].hasOwnProperty("event")) + { + let from = new TrailNode(this.actions[i].from.nodeId, this.actions[i].from.nodeType, this.actions[i].from.targetAnchor); + let to = new TrailNode(this.actions[i].to.nodeId, this.actions[i].to.nodeType, this.actions[i].to.targetAnchor); + let event = { "id": this.actions[i].event.id, "type": this.actions[i].event.type, "properties": this.actions[i].event.properties, "timestamp": new Date(this.actions[i].event.timestamp)}; + let agent = this.actions[i].agent; - actionArray.push(new Action({from, to, event, agent})); - } - else if(this.actions[i] && this.actions[i].hasOwnProperty("event") && this.actions[i].event.hasOwnProperty("timestamp")) - { - // we got event's id and timestamp - actionArray.push(new Action({event: {"id": i, "timestamp": new Date(this.actions[i].event.timestamp)}})); + actionArray.push(new Action({from, to, event, agent})); + } + else if(this.actions[i] && this.actions[i].hasOwnProperty("event") && this.actions[i].event.hasOwnProperty("timestamp")) + { + // we got event's id and timestamp + actionArray.push(new Action({event: {"id": i, "timestamp": new Date(this.actions[i].event.timestamp)}})); + } + else if(Array.isArray(this.actions)) + { + // all we got is an Array with the event's id + actionIds.push(this.actions[i]); + } + else + { + // all we got is event's id + actionIds.push(i); + } } - else if(Array.isArray(this.actions)) + + // sort action items before creating list + if (actionArray.length > 0) { - // all we got is an Array with the event's id - actionIds.push(this.actions[i]); + this.actions = new List(...sort(actionArray)); } - else + else if (actionIds.length > 0) { - // all we got is event's id - actionIds.push(i); + this.actions = actionIds; } } - - // sort action items before creating list - if (actionArray.length > 0) - { - this.actions = new List(...sort(actionArray)); - } - else if (actionIds.length > 0) - { - this.actions = actionIds; - } -} -function isValid(entity) -{ - let isValid = false; - if (entity && typeof (entity) === 'object' && !Array.isArray(entity)) + /** + * + * @param {Object} entity + * @returns {boolean} + */ + static isValid(entity) { - if (entity.hasOwnProperty('type') && entity.type === Types.TRAIL && - entity.hasOwnProperty('id') && entity.hasOwnProperty('parent')) + let isValid = false; + if (entity && typeof (entity) === 'object' && !Array.isArray(entity)) { - isValid = true; + if (entity.hasOwnProperty('type') && entity.type === Types.TRAIL && + entity.hasOwnProperty('id') && entity.hasOwnProperty('parent')) + { + isValid = true; + } } - } - return isValid; -} + return isValid; + } -function sort(actions = null) -{ - if (actions && Array.isArray(actions) && actions[0].event.timestamp.constructor === Date) + /** + * + * @param {Array | null} actions + * @returns {Array} + */ + static sort(actions = null) { - // sort and return object array based on timestamp - return actions.sort(function(action1, action2) + if (actions && Array.isArray(actions) && actions[0].event.timestamp.constructor === Date) { - return action1.event['timestamp'] - action2.event['timestamp']; - }); - } - else if (actions && Array.isArray(actions) && (actions[0].event.timestamp.constructor === String || actions[0].event.timestamp.constructor === Number)) - { - // sort Action array based on timestamp - return actions.sort(function(action1, action2) + // sort and return object array based on timestamp + return actions.sort(function(action1, action2) + { + return action1.event['timestamp'] - action2.event['timestamp']; + }); + } + else if (actions && Array.isArray(actions) && (actions[0].event.timestamp.constructor === String || actions[0].event.timestamp.constructor === Number)) { - return new Date(action1.event['timestamp']) - new Date(action2.event['timestamp']); - }); + // sort Action array based on timestamp + return actions.sort(function(action1, action2) + { + return new Date(action1.event['timestamp']) - new Date(action2.event['timestamp']); + }); + } } -} -Trail.prototype.toJSON = function () -{ - let actionArray = []; - - if (this.actions.constructor === List) + /** + * + * @returns {Object.} + * + */ + toJSON() { - //in case we have only action ids and timestamps - if(this.actions.head && (!this.actions.head.from && !this.actions.head.to && !this.actions.head.agent)) - { - let action = this.actions.head; + let actionArray = []; - while(action) + if (this.actions.constructor === List) + { + //in case we have only action ids and timestamps + if(this.actions.head && (!this.actions.head.from && !this.actions.head.to && !this.actions.head.agent)) { - actionArray.push(action.id); - action = action.next; + let action = this.actions.head; + + while(action) + { + actionArray.push(action.id); + action = action.next; + } + } + else { + actionArray = this.actions.toArray(); } } - else { - actionArray = this.actions.toArray(); + else if (Array.isArray(this.actions)) + { + actionArray = this.actions; } - } - else if (Array.isArray(this.actions)) - { - actionArray = this.actions; - } - - return { - id: this.id, - parent: this.parent, - properties: this.properties, - metaproperties: this.metaproperties, - interfaces: this.interfaces, - actions: actionArray, - type: this.type - }; -} -Trail.prototype.serialize = function () -{ - let actions; - if(this.actions instanceof Array) - { - actions = this.actions; - } - else - { - actions = this.actions.toArray(); - } - - return { + return { id: this.id, parent: this.parent, properties: this.properties, metaproperties: this.metaproperties, interfaces: this.interfaces, - actions: actions, + actions: actionArray, type: this.type - }; -} + }; + } + + /** + * + * @returns {Object.} + */ + serialize() + { + let actions; + if(this.actions instanceof Array) + { + actions = this.actions; + } + else + { + actions = this.actions.toArray(); + } + return { + id: this.id, + parent: this.parent, + properties: this.properties, + metaproperties: this.metaproperties, + interfaces: this.interfaces, + actions: actions, + type: this.type + }; + } +} -// TrailNode is basically an envelope for a hknode and a target anchor -class TrailNode { - constructor(nodeId, nodeType, targetAnchor) { +/** + * TrailNode is basically an envelope for a hknode and a target anchor. + */ +Trail.TrailNode = class TrailNode + { + /** + * Constructs a new Trail object. + * + * @param {string} [nodeId] Some id string for this TrailNode. + * @param {string} [nodeType] Type of the TrailNode. + * @param {string} [targetAnchor] + */ + constructor(nodeId, nodeType, targetAnchor) + { + /** + * + * Id of this TrailNode. + * + * @public + * @type {string} + * + */ this.nodeId = nodeId; + + /** + * + * Type of this TrailNode. + * + * @public + * @type {string} + * + */ this.nodeType = nodeType; + + /** + * @public + * @type {string} + */ this.targetAnchor = targetAnchor; } - toString() { + /** + * + * @return {string} + */ + toString() + { return this.nodeId + "#" + this.targetAnchor; } @@ -470,10 +678,18 @@ class TrailNode { // } } -// actions hold references to source and destination trail -// nodes (along with their respective target anchors), -// the related event and an agent (user, system or content). -class Action extends Item { +/** + * actions hold references to source and destination trail + * nodes (along with their respective target anchors), + * the related event and an agent (user, system or content). + */ +Trail.Action = class Action extends Item + { + /** Constructs a new Action object. + * + * @param {Object<{from: Trail.Action | null, to: Trail.Action | null, event: Object | null, agent: string | null}>} + * + */ constructor({from = null, to = null, event = {}, agent = null} = {}) { super(); @@ -501,11 +717,19 @@ class Action extends Item { } } + /** + * + * @returns {number} + */ getTime() { return new Date(this.event['timestamp']).getTime(); } + /** + * + * @returns {string} + */ toString() { if(this.from && this.to && this.agent && this.event && this.event.id ) @@ -518,6 +742,10 @@ class Action extends Item { } } + /** + * + * @returns {Object.} + */ toJSON() { if(this.from && this.to && this.agent && this.event && this.event.id ) @@ -532,9 +760,8 @@ class Action extends Item { } Trail.type = Types.TRAIL; -Trail.isValid = isValid; -Trail.sort = sort; -Trail.Action = Action; -Trail.TrailNode = TrailNode; - +const isValid = Trail.isValid; +const sort = Trail.sort; +const Action = Trail.Action; +const TrailNode = Trail.TrailNode; module.exports = Trail; From c8e1c1f1362049bbe874e26876ce7e34c68daf80 Mon Sep 17 00:00:00 2001 From: Gabriela Pinheiro Date: Tue, 19 Jul 2022 18:13:20 -0300 Subject: [PATCH 24/24] edited linked-list import --- package.json | 2 +- src/trail.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3820d7c..ba28f46 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "request": "^2.88.0", "request-promise-native": "^1.0.8", "shortid": "^2.2.14", - "linked-list": "file:src/linked-list" + "linked-list": "3.0.2" }, "license": "MIT", "repository": { diff --git a/src/trail.js b/src/trail.js index 969d739..d23e156 100644 --- a/src/trail.js +++ b/src/trail.js @@ -5,7 +5,7 @@ 'use strict' -const {List, Item} = require('linked-list'); +import {List, Item} from 'linked-list'; const Types = require('./types'); const HKEntity = require('./hkentity');