UNPKG

lincd

Version:

LINCD is a JavaScript library for building user interfaces with linked data (also known as 'structured data', or RDF)

381 lines 15.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestStore = exports.InMemoryStore = void 0; const globals_1 = require("@jest/globals"); const LinkedStorage_js_1 = require("../utils/LinkedStorage.js"); const models_js_1 = require("../models.js"); const QuadSet_js_1 = require("../collections/QuadSet.js"); const rdfs_js_1 = require("../ontologies/rdfs.js"); const rdf_js_1 = require("../ontologies/rdf.js"); const Shape_js_1 = require("../shapes/Shape.js"); const QuadArray_js_1 = require("../collections/QuadArray.js"); const SHACL_js_1 = require("../shapes/SHACL.js"); const LocalQueryResolver_js_1 = require("../utils/LocalQueryResolver.js"); class InMemoryStore extends Shape_js_1.Shape { constructor(n) { super(n); } init() { // console.log('Init ' + this.toString()); if (!this.initPromise) { this.initPromise = this.loadContents(); } return this.initPromise; } loadContents() { //by default an in-memory store starts empty - it has no permanent storage //overwrite this method to change that this.contents = new QuadSet_js_1.QuadSet(); return Promise.resolve(this.contents); } /** * returns the contents of the InMemoryStore as a QuadSet * do NOT modify the returned QuadSet directly. Add or remove contents to this store instead */ getContents() { return this.contents; } updateQuery(query) { return Promise.resolve((0, LocalQueryResolver_js_1.updateLocal)(query)); } createQuery(q) { return Promise.resolve((0, LocalQueryResolver_js_1.createLocal)(q)); } deleteQuery(query) { return Promise.resolve((0, LocalQueryResolver_js_1.deleteLocal)(query)); } update(toAdd, toRemove) { return this.init().then(() => { if (toAdd) { this._addMultiple(toAdd); } if (toRemove) { this._deleteMultiple(toRemove); } //this method should only get called if either toAdd or toRemove is not empty return this.onContentsUpdated(); }); } getDefaultGraph() { //NOTE: we changed this from a specific graph for each store BACK null, which means things will be stored in the default graph // because storing quads in multiple graphs easily becomes error-prone. // Removing a triple from one store might not remove it from the main graph. // for example, auth will store users serialised as JSONLD in sessions, these quads get added back to the main graph // until we have a better solution for this, we aim to only use the default graph as much as possible (which means no specific graph os used, thus return null) // return null; // return defaultGraph; return models_js_1.Graph.getOrCreate(this.namedNode.uri); } add(quad) { return this.init().then(() => { this.addNewContents(new QuadArray_js_1.QuadArray(quad)); this.onContentsUpdated(); return Promise.resolve(true); }); } addMultiple(quads) { return this.init().then(() => { this._addMultiple(quads); this.onContentsUpdated(); return Promise.resolve(true); }); } delete(quad) { return this.init().then(() => { // this.contents.delete(quad); this._deleteMultiple(new QuadArray_js_1.QuadArray(quad)); this.onContentsUpdated(); return true; }); } deleteMultiple(quads) { return this.init().then(() => { this._deleteMultiple(quads); this.onContentsUpdated(); return true; }); } clearProperties(subjectToPredicates) { return this.init().then(() => { let toDelete = new QuadSet_js_1.QuadSet(); this.contents.forEach((q) => { if (subjectToPredicates.has(q.subject) && subjectToPredicates.get(q.subject).has(q.predicate)) { toDelete.add(q); } }); if (toDelete.size > 0) { return this.deleteMultiple(toDelete); } return false; }); } setURIs(nodeToCurrentUriMap) { //by default an in-memory store does not support setting URIs, // if it's used in a local context for in memory storage it doesn't really care about permanent storage URI's // if it's used in a different context, this method should be overwritten return Promise.resolve([]); } removeNodes(nodes) { //when storage calls removeNodes, all quads have already been removed locally //and an in memory store always holds all data in memory, //so there is nothing to do here return Promise.resolve(true); } selectQuery(query) { return Promise.resolve((0, LocalQueryResolver_js_1.resolveLocal)(query)).catch((e) => { console.error('Error in query', e); return new QuadArray_js_1.QuadArray(); }); } loadShape(shapeInstance, request) { return this.init().then(() => { return this.getRequestQuads(shapeInstance, request); //for testing: add timer // return new Promise((resolve, reject) => { // setTimeout(() => { //get quads for each (nested) shape / property shape // let quads = this.getRequestQuads(shapeInstance, request); // return resolve(quads); // }, 1500); // }); }); } loadShapes(shapeInstances, request) { return this.init().then(() => { let quads = new QuadArray_js_1.QuadArray(); shapeInstances.forEach((shapeInstance) => { this.getRequestQuads(shapeInstance, request, quads); }); return quads; // return new Promise((resolve, reject) => { // setTimeout(() => { // //get quads for each (nested) shape / property shape // //TODO: update getRequestQuads to work with shape set // // possibly even merge definitions of loadShape and loadShapes? is it select? query? // // let quads = this.getRequestQuads(shapeInstances, request); // // return Promise.resolve(quads); // // return resolve(quads); // return resolve([] as any); // }, 1500); // }); }); } onContentsUpdated() { //by default in memory store does nothing here. But extending classes could choose to sync to a more permanent form of storage return Promise.resolve(false); } addNewContents(quads) { //get the target graph for this store, if configured let graph = LinkedStorage_js_1.LinkedStorage.getGraphForStore(this) || this.targetGraph || models_js_1.defaultGraph; //if there is one, move the quads into that graph if (graph) { quads = quads.moveTo(graph, false); } this.contents.addFrom(quads); return quads; } _addMultiple(quads) { this.addNewContents(quads); // this.contents = this.contents.concat(quads); } _deleteMultiple(quads) { //first we add the quads to the right graph (which effectively ADDS these quads to this store) //then we remove them from the contents //get the target graph for this store, if configured let graph = LinkedStorage_js_1.LinkedStorage.getGraphForStore(this) || this.targetGraph || models_js_1.defaultGraph; //if there is one, move the quads into that graph if (graph) { quads = quads.moveTo(graph, false); } quads.forEach((quad) => { this.contents.delete(quad); quad.remove(false); }); } getRequestQuads(source, request, quads = new QuadArray_js_1.QuadArray()) { // let {shape, properties}: {shape: typeof Shape; properties?: (PropertyShape | BoundPropertyShapes)[]} = request; request.forEach((propertyRequest) => { let subRequest; let propertyShape; let propertyShapeSource; //if an entry is an array, then it consists of the property shape + a sub request if (Array.isArray(propertyRequest)) { [propertyShape, subRequest] = propertyRequest; } else if (propertyRequest instanceof SHACL_js_1.PropertyShape) { propertyShape = propertyRequest; } if (propertyShape) { let path = propertyShape.path; if (path instanceof models_js_1.NamedNode) { if (source instanceof QuadSet_js_1.QuadSet) { propertyShapeSource = source .getObjects() .getQuads(path); } else if (source instanceof Shape_js_1.Shape) { propertyShapeSource = source.getQuads(path); } } else { propertyShapeSource = source; for (let i = 0; i < path.length; i++) { if (propertyShapeSource instanceof QuadSet_js_1.QuadSet) { propertyShapeSource = propertyShapeSource .getObjects() .getQuads(path[i]); } else if (propertyShapeSource instanceof Shape_js_1.Shape) { propertyShapeSource = propertyShapeSource.getQuads(path[i]); } } } propertyShapeSource.forEach((q) => quads.push(q)); } if (subRequest) { this.getRequestQuads(propertyShapeSource, subRequest, quads); } }); return quads; } } exports.InMemoryStore = InMemoryStore; class TestStore { constructor() { this.defaultGraph = models_js_1.Graph.create(); this.contents = new QuadSet_js_1.QuadSet(); } init() { return null; } reset() { this.contents = new QuadSet_js_1.QuadSet(); } update(added, removed) { added.forEach((q) => this.contents.add(q)); removed.forEach((q) => this.contents.delete(q)); return null; } selectQuery(query) { return null; } add(quad) { return null; } addMultiple(quads) { return null; } delete(quad) { return null; } deleteMultiple(quads) { return null; } setURIs(nodeToCurrentUriMap) { return null; } getDefaultGraph() { return this.defaultGraph; } removeNodes(nodes) { return null; } loadShape(shapeInstance, request) { return null; } loadShapes(shapeSet, request) { return null; } clearProperties(subjectToPredicates) { let deleted = false; this.contents.forEach((q) => { if (subjectToPredicates.has(q.subject) && subjectToPredicates.get(q.subject).has(q.predicate)) { this.contents.delete(q); deleted = true; } }); return Promise.resolve(deleted); } } exports.TestStore = TestStore; let store = new TestStore(); LinkedStorage_js_1.LinkedStorage.setDefaultStore(store); (0, globals_1.describe)('default store', () => { (0, globals_1.test)('does not store temporary node', () => { let node = models_js_1.NamedNode.create(); node.setValue(rdfs_js_1.rdfs.label, 'test'); (0, globals_1.expect)(store.contents.size).toBe(0); }); (0, globals_1.test)('stores quads of saved node', () => __awaiter(void 0, void 0, void 0, function* () { store.reset(); let node = models_js_1.NamedNode.create(); node.setValue(rdfs_js_1.rdfs.label, 'test2'); node.save(); try { yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); (0, globals_1.expect)(store.contents.size).toBe(1); } catch (e) { console.warn('Why err?', e); } })); (0, globals_1.test)('stores new properties of existing node', () => __awaiter(void 0, void 0, void 0, function* () { store.reset(); let node = models_js_1.NamedNode.create(); node.isTemporaryNode = false; node.setValue(rdfs_js_1.rdfs.label, 'test3'); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); (0, globals_1.expect)(store.contents.size).toBe(1); })); (0, globals_1.test)('unsetAll removes those properties from store', () => __awaiter(void 0, void 0, void 0, function* () { store.reset(); let node = models_js_1.NamedNode.create(); node.isTemporaryNode = false; node.setValue(rdfs_js_1.rdfs.label, 'test4'); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); node.unsetAll(rdfs_js_1.rdfs.label); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); (0, globals_1.expect)(store.contents.size).toBe(0); })); (0, globals_1.test)('unset removes that property from store', () => __awaiter(void 0, void 0, void 0, function* () { store.reset(); let node = models_js_1.NamedNode.create(); node.isTemporaryNode = false; node.setValue(rdfs_js_1.rdfs.label, 'test4'); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); node.unset(rdfs_js_1.rdfs.label, new models_js_1.Literal('test4')); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); (0, globals_1.expect)(store.contents.size).toBe(0); })); (0, globals_1.test)('remove node & properties from store', () => __awaiter(void 0, void 0, void 0, function* () { store.reset(); let node = models_js_1.NamedNode.create(); node.isTemporaryNode = false; node.setValue(rdfs_js_1.rdfs.label, 'test5'); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); node.remove(); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); (0, globals_1.expect)(store.contents.size).toBe(0); })); (0, globals_1.test)('promiseUpdated waits for both storing nodes and altering nodes to complete', () => __awaiter(void 0, void 0, void 0, function* () { store.reset(); let node = models_js_1.NamedNode.create(); node.setValue(rdfs_js_1.rdfs.label, 'test5'); node.save(); node.set(rdf_js_1.rdf.type, node); yield LinkedStorage_js_1.LinkedStorage.promiseUpdated(); (0, globals_1.expect)(store.contents.size).toBe(2); })); }); //# sourceMappingURL=storage.test.js.map