From a691a1b4e2e5c4bcf4710c51330c381451c7a874 Mon Sep 17 00:00:00 2001 From: srosset81 Date: Tue, 5 Aug 2025 15:53:56 +0200 Subject: [PATCH] Use normalized quads for PUT diff --- .../packages/activitypub/package.json | 2 +- src/middleware/packages/crypto/package.json | 2 +- .../packages/jsonld/services/parser/index.js | 5 ++ src/middleware/packages/ldp/package.json | 2 +- .../ldp/services/resource/actions/put.js | 11 ++-- .../packages/ldp/services/resource/methods.js | 15 ++--- src/middleware/packages/solid/package.json | 2 +- .../packages/sparql-endpoint/package.json | 2 +- src/middleware/tests/ldp/resource.test.js | 27 +-------- src/middleware/tests/package.json | 2 +- src/middleware/yarn.lock | 55 ++++++++++++++++++- 11 files changed, 80 insertions(+), 45 deletions(-) diff --git a/src/middleware/packages/activitypub/package.json b/src/middleware/packages/activitypub/package.json index 4461e8c50..0215e51b6 100644 --- a/src/middleware/packages/activitypub/package.json +++ b/src/middleware/packages/activitypub/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "author": "Virtual Assembly", "dependencies": { - "@rdfjs/data-model": "^1.3.4", + "@rdfjs/data-model": "^2.1.1", "@semapps/crypto": "1.1.4", "@semapps/ldp": "1.1.4", "@semapps/middlewares": "1.1.4", diff --git a/src/middleware/packages/crypto/package.json b/src/middleware/packages/crypto/package.json index c03fbd26b..e43973f53 100644 --- a/src/middleware/packages/crypto/package.json +++ b/src/middleware/packages/crypto/package.json @@ -12,7 +12,7 @@ "@digitalbazaar/ed25519-verification-key-2020": "^4.2.0", "@digitalbazaar/eddsa-rdfc-2022-cryptosuite": "^1.2.0", "@digitalbazaar/vc": "^7.1.0", - "@rdfjs/data-model": "^1.3.4", + "@rdfjs/data-model": "^2.1.1", "@semapps/ldp": "1.1.4", "@semapps/middlewares": "1.1.4", "@semapps/mime-types": "1.1.4", diff --git a/src/middleware/packages/jsonld/services/parser/index.js b/src/middleware/packages/jsonld/services/parser/index.js index 01d6729d0..7c7c7d60c 100644 --- a/src/middleware/packages/jsonld/services/parser/index.js +++ b/src/middleware/packages/jsonld/services/parser/index.js @@ -42,6 +42,11 @@ module.exports = { const { input, options } = ctx.params; return this.jsonld.normalize(input, options); }, + async normalizeToQuads(ctx) { + const { input, options } = ctx.params; + const normalizedQuads = await this.jsonld.normalize(input, options); + return this.rdfToQuads(normalizedQuads, 'application/n-quads'); + }, async fromRDF(ctx) { const { input, options = {} } = ctx.params; const { format } = options; diff --git a/src/middleware/packages/ldp/package.json b/src/middleware/packages/ldp/package.json index 323a912e4..0af6d8409 100644 --- a/src/middleware/packages/ldp/package.json +++ b/src/middleware/packages/ldp/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "author": "Virtual Assembly", "dependencies": { - "@rdfjs/data-model": "^1.3.4", + "@rdfjs/data-model": "^2.1.1", "@semapps/middlewares": "1.1.4", "@semapps/mime-types": "1.1.4", "@semapps/ontologies": "1.1.4", diff --git a/src/middleware/packages/ldp/services/resource/actions/put.js b/src/middleware/packages/ldp/services/resource/actions/put.js index 9cba90c4c..8dff1ba94 100644 --- a/src/middleware/packages/ldp/services/resource/actions/put.js +++ b/src/middleware/packages/ldp/services/resource/actions/put.js @@ -1,3 +1,4 @@ +const { diffLines } = require('diff'); const { MoleculerError } = require('moleculer').Errors; const { MIME_TYPES } = require('@semapps/mime-types'); const { cleanUndefined } = require('../../../utils'); @@ -61,20 +62,20 @@ module.exports = { }; } - let oldTriples = await ctx.call('jsonld.parser.toQuads', { input: oldData }); - let newTriples = await ctx.call('jsonld.parser.toQuads', { input: resource }); + let oldTriples = await ctx.call('jsonld.parser.normalizeToQuads', { input: oldData }); + let newTriples = await ctx.call('jsonld.parser.normalizeToQuads', { input: resource }); // Filter out triples whose subject is not the resource itself // We don't want to update or delete resources with IDs - oldTriples = this.filterOtherNamedNodes(oldTriples, resourceUri); - newTriples = this.filterOtherNamedNodes(newTriples, resourceUri); + // oldTriples = this.filterOtherNamedNodes(oldTriples, resourceUri); + // newTriples = this.filterOtherNamedNodes(newTriples, resourceUri); // blank nodes are convert to variable for sparql query (?variable) oldTriples = this.convertBlankNodesToVars(oldTriples); newTriples = this.convertBlankNodesToVars(newTriples); // same values blackNodes removing because those duplicated values blank nodes cause indiscriminate blank resultings in bug wahen trying to delete both - newTriples = this.removeDuplicatedVariables(newTriples); + // newTriples = this.removeDuplicatedVariables(newTriples); // Triples to add are reversed, so that blank nodes are linked to resource before being assigned data properties // Triples to remove are not reversed, because we want to remove the data properties before unlinking it from the resource diff --git a/src/middleware/packages/ldp/services/resource/methods.js b/src/middleware/packages/ldp/services/resource/methods.js index 1e6afcf47..1b29be2da 100644 --- a/src/middleware/packages/ldp/services/resource/methods.js +++ b/src/middleware/packages/ldp/services/resource/methods.js @@ -1,7 +1,8 @@ const fs = require('fs'); const bytes = require('bytes'); -const { variable } = require('@rdfjs/data-model'); +const dataFactory = require('@rdfjs/data-model').default; const { MoleculerError } = require('moleculer').Errors; +const { variable, quad } = dataFactory; // TODO put each method in a different file (problems with "this" not working) module.exports = { @@ -31,14 +32,14 @@ module.exports = { return triples.filter(triple => !(triple.subject.termType === 'NamedNode' && triple.subject.value !== resourceUri)); }, convertBlankNodesToVars(triples) { - return triples.map(triple => { - if (triple.subject.termType === 'BlankNode') { - triple.subject = variable(triple.subject.value); + return triples.map(t => { + if (t.subject.termType === 'BlankNode') { + return quad(variable(t.subject.value), t.predicate, t.object, t.graph); } - if (triple.object.termType === 'BlankNode') { - triple.object = variable(triple.object.value); + if (t.object.termType === 'BlankNode') { + return quad(t.subject, t.predicate, variable(t.object.value), t.graph); } - return triple; + return t; }); }, // Exclude from triples1 the triples which also exist in triples2 diff --git a/src/middleware/packages/solid/package.json b/src/middleware/packages/solid/package.json index 66b87ae2e..da57a0703 100644 --- a/src/middleware/packages/solid/package.json +++ b/src/middleware/packages/solid/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "author": "Virtual Assembly", "dependencies": { - "@rdfjs/data-model": "^1.3.4", + "@rdfjs/data-model": "^2.1.1", "@semapps/activitypub": "1.1.4", "@semapps/ldp": "1.1.4", "@semapps/middlewares": "1.1.4", diff --git a/src/middleware/packages/sparql-endpoint/package.json b/src/middleware/packages/sparql-endpoint/package.json index b3149c47f..ded7b4b72 100644 --- a/src/middleware/packages/sparql-endpoint/package.json +++ b/src/middleware/packages/sparql-endpoint/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "author": "Virtual Assembly", "dependencies": { - "@rdfjs/data-model": "^1.3.4", + "@rdfjs/data-model": "^2.1.1", "@semapps/middlewares": "1.1.4", "@semapps/mime-types": "1.1.4", "@semapps/triplestore": "1.1.4", diff --git a/src/middleware/tests/ldp/resource.test.js b/src/middleware/tests/ldp/resource.test.js index a91910a52..624571a38 100644 --- a/src/middleware/tests/ldp/resource.test.js +++ b/src/middleware/tests/ldp/resource.test.js @@ -1,6 +1,7 @@ -const { quad, namedNode, blankNode, literal } = require('rdf-data-model'); +const dataFactory = require('rdf-data-model'); const CONFIG = require('../config'); const initialize = require('./initialize'); +const { quad, namedNode, blankNode, literal } = dataFactory; jest.setTimeout(50000); let broker; @@ -181,30 +182,6 @@ describe('Resource CRUD operations', () => { ]) }); - resourceUpdated.hasLocation = [ - { - label: 'Compiegne' - }, - { - label: 'Compiegne' - }, - { - label: 'Compiegne' - } - ]; - - await broker.call('ldp.resource.put', { resource: resourceUpdated }); - - updatedProject = await broker.call('ldp.resource.get', { resourceUri: project1['@id'] }); - - expect(updatedProject).toMatchObject({ - 'pair:description': 'myProjectUpdatedAgain', - 'pair:affiliates': 'http://localhost:3000/users/pierre', - 'pair:hasLocation': { - 'pair:label': 'Compiegne' - } - }); - resourceUpdated.hasLocation = [ { label: 'Compiegne', diff --git a/src/middleware/tests/package.json b/src/middleware/tests/package.json index 7a22501cf..2619a8249 100644 --- a/src/middleware/tests/package.json +++ b/src/middleware/tests/package.json @@ -8,7 +8,7 @@ "author": "SemApps Team", "license": "Apache-2.0", "dependencies": { - "@rdfjs/data-model": "^1.3.4", + "@rdfjs/data-model": "^2.1.1", "@semapps/activitypub": "1.1.4", "@semapps/auth": "1.1.4", "@semapps/core": "1.1.4", diff --git a/src/middleware/yarn.lock b/src/middleware/yarn.lock index 8716b8621..ce83eb05e 100644 --- a/src/middleware/yarn.lock +++ b/src/middleware/yarn.lock @@ -420,6 +420,13 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@bergos/jsonparse@^1.4.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@bergos/jsonparse/-/jsonparse-1.4.2.tgz#75ba0065ec448bf24ea8b3494944a1e3219c5e46" + integrity sha512-qUt0QNJjvg4s1zk+AuLM6s/zcsQ8MvGn7+1f0vPuxvpCYa08YtTryuDInngbEyW5fNGGYe2znKt61RMGd5HnXg== + dependencies: + buffer "^6.0.3" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -1731,7 +1738,12 @@ dependencies: "@rdfjs/types" ">=1.0.1" -"@rdfjs/types@*", "@rdfjs/types@>=1.0.0", "@rdfjs/types@>=1.0.1": +"@rdfjs/data-model@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@rdfjs/data-model/-/data-model-2.1.1.tgz#710a2ba88316a029f38912643db30646a125339d" + integrity sha512-6mcOI4DjIPS6MOZw23H8oAdujHCk5gippVNQ7mKwliYTvTNh+uqRM91B9OLqhoAoNcQ3t49Dx2ooIMRG9/6ooA== + +"@rdfjs/types@*", "@rdfjs/types@>=1.0.0", "@rdfjs/types@>=1.0.1", "@rdfjs/types@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@rdfjs/types/-/types-2.0.1.tgz#f10b50ceffff00b961c4265e60ac9f74550251da" integrity sha512-uyAzpugX7KekAXAHq26m3JlUIZJOC0uSBhpnefGV5i15bevDyyejoB7I+9MKeUrzXD8OOUI3+4FeV1wwQr5ihA== @@ -2103,6 +2115,13 @@ "@types/node" "*" safe-buffer "~5.1.1" +"@types/readable-stream@^4.0.0": + version "4.0.21" + resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-4.0.21.tgz#716558454a5e0c3c0651520f8154efc3288f59cb" + integrity sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ== + dependencies: + "@types/node" "*" + "@types/responselike@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" @@ -7224,6 +7243,16 @@ jsonld-context-parser@^2.0.0, jsonld-context-parser@^2.1.2, jsonld-context-parse http-link-header "^1.0.2" relative-to-absolute-iri "^1.0.5" +jsonld-context-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonld-context-parser/-/jsonld-context-parser-3.0.0.tgz#43992862fc3eabcee9940cf4c44bb2b0dbe2542c" + integrity sha512-Kg6TVtBUdIm057ht/8WNhM9BROt+BeYaDGXbzrKaa3xA99csee+CsD8IMCTizRgzoO8PIzvzcxxCoRvpq1xNQw== + dependencies: + "@types/http-link-header" "^1.0.1" + "@types/node" "^18.0.0" + http-link-header "^1.0.2" + relative-to-absolute-iri "^1.0.5" + jsonld-signatures@^11.2.1, jsonld-signatures@^11.3.0, jsonld-signatures@^11.3.2: version "11.5.0" resolved "https://registry.yarnpkg.com/jsonld-signatures/-/jsonld-signatures-11.5.0.tgz#020ad488bd1977c5169de3d2a4cc28a76dec2663" @@ -7246,7 +7275,7 @@ jsonld-signatures@^5.0.0: security-context "^4.0.0" serialize-error "^5.0.0" -jsonld-streaming-parser@^2.4.0, jsonld-streaming-parser@^2.4.2: +jsonld-streaming-parser@^2.4.0: version "2.4.3" resolved "https://registry.yarnpkg.com/jsonld-streaming-parser/-/jsonld-streaming-parser-2.4.3.tgz#d8faa1df6dc3b760fb9455602edc5f001d756ef0" integrity sha512-ysuevJ+l8+Y4W3J/yQW3pa9VCBNDHo2tZkKmPAnfhfsmFMyxuueAeXMmTbpJZdrpagzeeDVr3A8EZVuHliQJ9A== @@ -7259,6 +7288,21 @@ jsonld-streaming-parser@^2.4.0, jsonld-streaming-parser@^2.4.2: jsonparse "^1.3.1" rdf-data-factory "^1.1.0" +jsonld-streaming-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/jsonld-streaming-parser/-/jsonld-streaming-parser-5.0.0.tgz#995bd347b32dc02f97f1c1a5dc90d32fe12ee0ba" + integrity sha512-Q6Bfbmig8fFpIbJgJTi4LLzco9dz0YuBM/mDvUYXzP8L/+me6P3pRy4exrhCpv49Bwv2oQFFIHM7wIwCKma2XA== + dependencies: + "@bergos/jsonparse" "^1.4.0" + "@types/http-link-header" "^1.0.1" + "@types/readable-stream" "^4.0.0" + buffer "^6.0.3" + canonicalize "^1.0.1" + http-link-header "^1.0.2" + jsonld-context-parser "^3.0.0" + rdf-data-factory "^2.0.0" + readable-stream "^4.0.0" + jsonld-streaming-serializer@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsonld-streaming-serializer/-/jsonld-streaming-serializer-1.3.0.tgz#e42ec6ffefc5b80cc1bd17e8eee7943242f17a4d" @@ -9730,6 +9774,13 @@ rdf-data-factory@^1.1.0, rdf-data-factory@^1.1.2: dependencies: "@rdfjs/types" "^1.0.0" +rdf-data-factory@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/rdf-data-factory/-/rdf-data-factory-2.0.2.tgz#dfac1fdf99502f3b6d61f8e99e97af2490346e32" + integrity sha512-WzPoYHwQYWvIP9k+7IBLY1b4nIDitzAK4mA37WumAF/Cjvu/KOtYJH9IPZnUTWNSd5K2+pq4vrcE9WZC4sRHhg== + dependencies: + "@rdfjs/types" "^2.0.0" + rdf-data-model@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/rdf-data-model/-/rdf-data-model-1.0.0.tgz#e768e1c2cd904c186471b20d1a4fce77c7712a4b"