diff --git a/package-lock.json b/package-lock.json index 7c8ad6c8f..36b04f200 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rdflib", - "version": "2.2.33", + "version": "2.2.34-alpha", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rdflib", - "version": "2.2.33", + "version": "2.2.34-alpha", "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0", diff --git a/package.json b/package.json index 316cfb91a..2c61bc0a2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rdflib", "description": "an RDF library for node.js. Suitable for client and server side.", - "version": "2.2.33", + "version": "2.2.34-alpha", "private": false, "browserslist": [ "> 0.5%" diff --git a/src/fetcher.ts b/src/fetcher.ts index cb449c11c..712ff2aba 100644 --- a/src/fetcher.ts +++ b/src/fetcher.ts @@ -34,7 +34,7 @@ import rdfParse from './parse' import { parseRDFaDOM } from './rdfaparser' import RDFParser from './rdfxmlparser' import * as Uri from './uri' -import { isCollection, isNamedNode} from './utils/terms' +import { isNamedNode } from './utils/terms' import * as Util from './utils-js' import serialize from './serialize' import crossFetch, { Headers } from 'cross-fetch' @@ -1274,13 +1274,7 @@ export default class Fetcher implements CallbackifyInterface { now.getSeconds() + '.' + now.getMilliseconds() + '] ' + statusMessage // let kb = this.store - - const statusNode = kb.the(req, this.ns.link('status')) - if (isCollection(statusNode)) { - statusNode.append(kb.rdfFactory.literal(statusMessage)) - } else { - log.warn('web.js: No list to add to: ' + statusNode + ',' + statusMessage) - } + kb.add(req, this.ns.link('status'), kb.rdfFactory.literal(statusMessage), this.appNode) } /** @@ -1725,7 +1719,6 @@ export default class Fetcher implements CallbackifyInterface { // We store the docuri as a string, not as a node, // see https://github.com/linkeddata/rdflib.js/pull/427#pullrequestreview-447910061 kb.add(req, this.ns.link('requestedURI'), kb.rdfFactory.literal(docuri), this.appNode) - kb.add(req, this.ns.link('status'), kb.collection(), this.appNode) } saveResponseMetadata ( @@ -2004,7 +1997,11 @@ export default class Fetcher implements CallbackifyInterface { // Before we parse new data clear old but only on 200 if (options.clearPreviousData) { - kb.removeDocument(options.resource) + // kb.removeDocument(options.resource) + const sts = kb.statementsMatching(undefined, undefined, undefined, options.resource).slice() // Take a copy as this is the actual index + for (let i = 0; i < sts.length; i++) { + kb.removeStatement(sts[i]) + } } let isImage = contentType.includes('image/') || diff --git a/src/store.ts b/src/store.ts index 1b4714958..56ecebe58 100644 --- a/src/store.ts +++ b/src/store.ts @@ -871,25 +871,38 @@ export default class IndexedFormula extends Formula { // IN future - allow pass } /** - * Removes all statements in a doc, along with the related metadata including request/response + * Removes all metadata * @param doc - The document / graph */ - removeDocument(doc: Quad_Graph): IndexedFormula { - const meta = this.sym('chrome://TheCurrentSession') // or this.rdfFactory.namedNode('chrome://TheCurrentSession') - const linkNamespaceURI = 'http://www.w3.org/2007/ont/link#' // alain - // remove request/response and metadata - const requests = this.statementsMatching(undefined, this.sym(`${linkNamespaceURI}requestedURI`), this.rdfFactory.literal(doc.value), meta).map(st => st.subject) + removeMetadata(doc: Quad_Graph): IndexedFormula { + const meta = this.fetcher?.appNode // this.sym('chrome://TheCurrentSession') + const linkNamespaceURI = 'http://www.w3.org/2007/ont/link#' + const requests = this.statementsMatching(null, this.sym(`${linkNamespaceURI}requestedURI`), this.rdfFactory.literal(doc.value), meta).map(st => st.subject) for (var r = 0; r < requests.length; r++) { const request = requests[r] - if (request !== undefined) { - this.removeMatches(request, null, null, meta) + if (request != null) { // loose equality for null and undefined const response = this.any(request, this.sym(`${linkNamespaceURI}response`), null, meta) as Quad_Subject - if (response !== undefined) { // ts + if (response != null) { this.removeMatches(response, null, null, meta) } + const status = this.any(request, this.sym(`${linkNamespaceURI}status`), null, meta) as Quad_Subject + if (status != null) { + this.removeMatches(status, null, null, meta) + } + this.removeMatches(request, null, null, meta) } } - this.removeMatches(this.sym(doc.value), null, null, meta) // content-type + this.removeMatches(doc as Quad_Subject, null, null, meta) + return this + } + + /** + * Removes all statements in a doc, along with the related metadata including request/response + * @param doc - The document / graph + */ + removeDocument(doc: Quad_Graph): IndexedFormula { + // remove request/response and metadata + this.removeMetadata(doc) // remove document var sts: Quad[] = this.statementsMatching(undefined, undefined, undefined, doc).slice() // Take a copy as this is the actual index diff --git a/tests/unit/indexed-formula-test.js b/tests/unit/indexed-formula-test.js index 4501d55f2..0c4f3388c 100644 --- a/tests/unit/indexed-formula-test.js +++ b/tests/unit/indexed-formula-test.js @@ -7,6 +7,8 @@ import IndexedFormula from '../../src/store' import NamedNode from '../../src/named-node' import { RDFArrayRemove } from '../../src/utils-js' import DataFactory from '../../src/factories/rdflib-data-factory' +import parse from '../../src/parse' +import serialize from '../../src/serialize' describe('IndexedFormula', () => { const g0 = NamedNode.fromValue('https://example.com/graph0') @@ -319,25 +321,98 @@ describe('IndexedFormula', () => { }); describe('removeMatches', () => { - it ('removes matching statements', () => { - const store = new IndexedFormula() - store.add([triple2, triple3, triple4]) - expect(store.statements.length).to.eq(3) - store.removeMatches(null, null, triple3.object, null) - - expect(store.statements.length).to.eq(1) - expect(store.holds(s2, p2, o2)).to.be.true() - }) - it ('does the same as remove of matches', () => { - const store0 = new IndexedFormula() - store0.add([triple2, triple3, triple4]) - const store1 = new IndexedFormula() - store1.add([triple2, triple3, triple4]) - store0.remove(store0.match(null, null, triple3.object, null)) - store1.removeMatches(null, null, triple3.object, null) - - expect(store0.holdsStatement(store1)).to.be.true() - expect(store1.holdsStatement(store0)).to.be.true() - }) - }); -}) + it ('removes matching statements', () => { + const store = new IndexedFormula() + store.add([triple2, triple3, triple4]) + expect(store.statements.length).to.eq(3) + store.removeMatches(null, null, triple3.object, null) + + expect(store.statements.length).to.eq(1) + expect(store.holds(s2, p2, o2)).to.be.true() + }) + it ('does the same as remove of matches', () => { + const store0 = new IndexedFormula() + store0.add([triple2, triple3, triple4]) + const store1 = new IndexedFormula() + store1.add([triple2, triple3, triple4]) + store0.remove(store0.match(null, null, triple3.object, null)) + store1.removeMatches(null, null, triple3.object, null) + + expect(store0.holdsStatement(store1)).to.be.true() + expect(store1.holdsStatement(store0)).to.be.true() + }) + }) + describe('removeMetadata', () => { + + }) + describe('removeDocument', () => { + const store = new IndexedFormula() + const meta = store.sym('chrome://TheCurrentSession') + const prefixes = `@prefix : <#>. + @prefix http: . + @prefix httph: . + @prefix tabont: . + @prefix rdfs: . + @prefix pro: . + @prefix tur: . + + pro:card a tabont:Document, tabont:RDFDocument, tur:Resource. + +` + const metaContent = prefixes + ` + [ + rdfs:label "[0:16:34] Request for https://bob.localhost:8443/profile/card"; + tabont:requestedURI "https://bob.localhost:8443/profile/card"; + tabont:response + [ + http:status 200; + http:statusText "OK"; + httph:accept-patch + "text/n3, application/sparql-update, application/sparql-update-single-match"; + httph:accept-post "*/*"; + httph:accept-put "*/*"; + httph:access-control-allow-credentials "true"; + httph:access-control-expose-headers + "Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Accept-Put, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By"; + httph:allow "OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE"; + httph:connection "keep-alive"; + httph:content-type "text/turtle"; + httph:date "Thu, 08 Feb 2024 23:16:35 GMT"; + httph:keep-alive "timeout=5"; + httph:link + '; rel=\"acl\", ; rel=\"describedBy\", ; rel=\"type\"'; + httph:ms-author-via "SPARQL"; + httph:transfer-encoding "chunked"; + httph:updates-via "wss://bob.localhost:8443"; + httph:vary "Accept, Authorization, Origin"; + httph:wac-allow 'user=\"read write append control\",public=\"read\"'; + httph:x-powered-by "solid-server/5.7.9-beta" + ]; + tabont:status + ( "[0:16:35.259] N3 parsed: 13 triples in 26 lines." + "[0:16:35.259] Done." ) + ]. + ` + const voidDoc = `@prefix : <#>. + +` + it ('removeMetada', () => { + parse(metaContent, store, meta.value, 'text/turtle') + store.removeMetadata(store.sym('https://bob.localhost:8443/profile/card')) + expect(serialize(meta, store, meta.uri)).to.eql(voidDoc) + }) + it ('removeDocument', () => { + parse(metaContent, store, meta.value, 'text/turtle') + const doc = store.sym('https://bob.localhost:8443/profile/card') + const docContent = ` + @prefix rdf: . + + <#test> <#value> [ rdf:first 1; rdf:rest [ rdf:first 2; rdf:rest [ rdf:first 3; rdf:rest rdf:nil ]]] . +` + parse(docContent, store, doc.uri, 'text/turtle') + store.removeDocument(store.sym('https://bob.localhost:8443/profile/card')) + expect(serialize(meta, store, meta.uri)).to.eql(voidDoc) + expect(serialize(doc, store, doc.uri)).to.eql(voidDoc) + }) + }) +}) \ No newline at end of file diff --git a/tests/unit/update-manager-test.js b/tests/unit/update-manager-test.js index 9320e6519..ffd1bde67 100644 --- a/tests/unit/update-manager-test.js +++ b/tests/unit/update-manager-test.js @@ -36,11 +36,26 @@ const st3 = $rdf.st(baz, p, 333, doc2) const httpResultsText = ` @prefix httph: . @prefix link: . - [] link:requestedURI "${doc.uri}", "${doc2.uri}"; link:response [ httph:accept-patch "application/sparql-update" ]. - ` +[ link:requestedURI "${doc.uri}"; + link:response + [ httph:accept-patch "application/sparql-update"; + httph:accept-post "*/*"; + httph:accept-put "*/*" + ]; + link:status + "[0:16:35.259] N3 parsed: 13 triples in 26 lines.", + "[0:16:35.259] Done." +]. +[ link:requestedURI "${doc2.uri}"; + link:response [ httph:accept-patch "application/sparql-update" ]; + link:status + ( "[0:16:35.259] N3 parsed: 13 triples in 26 lines." + "[0:16:35.259] Done." ) +]. +` function loadMeta (store) { - $rdf.parse(httpResultsText, store, meta.uri) + $rdf.parse(httpResultsText, store, meta.uri) // alain , 'text/turtle') console.log('Loaded metadata') } @@ -223,6 +238,17 @@ describe('UpdateManager', () => { expect(updater.editable(doc1)).to.equal(undefined) }) + it('Should not detect a document is editable from metadata after removeMetadata', () => { + loadMeta(updater.store) + updater.store.removeMetadata(doc1) + expect(updater.editable(doc1)).to.equal(undefined) + }) + + it('Should not detect a document is editable from metadata after removeDocument', () => { + loadMeta(updater.store) + updater.store.removeDocument(doc1) + expect(updater.editable(doc1)).to.equal(undefined) + }) it('Async version should detect a document is editable from metadata', async () => { loadMeta(updater.store) @@ -233,13 +259,9 @@ describe('UpdateManager', () => { it('Async version should not detect a document is editable from metadata after flush', async () => { loadMeta(updater.store) - expect(updater.editable(doc1)).to.equal('SPARQL') - updater.flagAuthorizationMetadata() - const result = await updater.checkEditable(doc1) - expect(result).to.equal(undefined) }) @@ -248,4 +270,4 @@ describe('UpdateManager', () => { -}) +}) \ No newline at end of file