From 4581242245d6b90a5209f19aa87cccf1c7f32624 Mon Sep 17 00:00:00 2001 From: SarthakDudhe Date: Wed, 6 May 2026 19:49:38 +0530 Subject: [PATCH 1/2] Add storage description link discovery --- lib/header.mjs | 27 ++++--- test/integration/http-test.mjs | 134 +++++++++++++++++++++------------ 2 files changed, 105 insertions(+), 56 deletions(-) diff --git a/lib/header.mjs b/lib/header.mjs index ed748623e..d0ce01b95 100644 --- a/lib/header.mjs +++ b/lib/header.mjs @@ -5,8 +5,10 @@ import debug from './debug.mjs' import { pathBasename } from './utils.mjs' import HTTPError from './http-error.mjs' -const MODES = ['Read', 'Write', 'Append', 'Control'] -const PERMISSIONS = MODES.map(m => m.toLowerCase()) +const MODES = ['Read', 'Write', 'Append', 'Control'] +const PERMISSIONS = MODES.map(m => m.toLowerCase()) +const STORAGE_DESCRIPTION = 'http://www.w3.org/ns/solid/terms#storageDescription' +const STORAGE_DESCRIPTION_METHODS = ['GET', 'HEAD', 'OPTIONS'] export function addLink (res, value, rel) { const oldLink = res.get('Link') @@ -72,13 +74,20 @@ export async function linksHandler (req, res, next) { if (fileMetadata.isContainer && req.method === 'OPTIONS') { res.header('Accept-Post', '*/*') } - // Add ACL and Meta Link in header - addLink(res, pathBasename(req.path) + ldp.suffixAcl, 'acl') - addLink(res, pathBasename(req.path) + ldp.suffixMeta, 'describedBy') - // Add other Link headers - addLinks(res, fileMetadata) - next() -} + // Add ACL and Meta Link in header + addLink(res, pathBasename(req.path) + ldp.suffixAcl, 'acl') + addLink(res, pathBasename(req.path) + ldp.suffixMeta, 'describedBy') + if (STORAGE_DESCRIPTION_METHODS.includes(req.method) && !isAuxiliaryResource(req.path, ldp)) { + addLink(res, ldp.resourceMapper.resolveUrl(req.hostname, '/' + ldp.suffixMeta), STORAGE_DESCRIPTION) + } + // Add other Link headers + addLinks(res, fileMetadata) + next() +} + +function isAuxiliaryResource (resourcePath, ldp) { + return resourcePath.endsWith(ldp.suffixAcl) || resourcePath.endsWith(ldp.suffixMeta) +} export function parseMetadataFromHeader (linkHeader) { const fileMetadata = new metadata.Metadata() diff --git a/test/integration/http-test.mjs b/test/integration/http-test.mjs index cac2c886f..51d532ad3 100644 --- a/test/integration/http-test.mjs +++ b/test/integration/http-test.mjs @@ -9,9 +9,10 @@ import { assert, expect } from 'chai' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -const suffixAcl = '.acl' -const suffixMeta = '.meta' -const server = setupSupertestServer({ +const suffixAcl = '.acl' +const suffixMeta = '.meta' +const storageDescription = 'http://www.w3.org/ns/solid/terms#storageDescription' +const server = setupSupertestServer({ live: true, dataBrowserPath: 'default', root: path.join(__dirname, '../resources'), @@ -162,13 +163,20 @@ describe('HTTP APIs', function () { .end(done) }) - it('should have set acl and describedBy Links for resource', - function (done) { - server.options('/sampleContainer2/example1.ttl') - .expect(hasHeader('acl', 'example1.ttl' + suffixAcl)) - .expect(hasHeader('describedBy', 'example1.ttl' + suffixMeta)) - .end(done) - }) + it('should have set acl and describedBy Links for resource', + function (done) { + server.options('/sampleContainer2/example1.ttl') + .expect(hasHeader('acl', 'example1.ttl' + suffixAcl)) + .expect(hasHeader('describedBy', 'example1.ttl' + suffixMeta)) + .end(done) + }) + + it('should have set storageDescription Link for resource', + function (done) { + server.options('/sampleContainer2/example1.ttl') + .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .end(done) + }) it('should have set Link as resource', function (done) { server.options('/sampleContainer2/example1.ttl') @@ -198,12 +206,18 @@ describe('HTTP APIs', function () { .end(done) }) - it('should have set acl and describedBy Links for container', function (done) { - server.options('/sampleContainer2/') - .expect(hasHeader('acl', suffixAcl)) - .expect(hasHeader('describedBy', suffixMeta)) - .end(done) - }) + it('should have set acl and describedBy Links for container', function (done) { + server.options('/sampleContainer2/') + .expect(hasHeader('acl', suffixAcl)) + .expect(hasHeader('describedBy', suffixMeta)) + .end(done) + }) + + it('should have set storageDescription Link for container', function (done) { + server.options('/sampleContainer2/') + .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .end(done) + }) }) describe('Not allowed method should return 405 and allow header', function (done) { @@ -265,14 +279,21 @@ describe('HTTP APIs', function () { .expect('updates-via', /wss?:\/\//) .expect(200, done) }) - it('should have set acl and describedBy Links for resource', - function (done) { - server.get('/sampleContainer2/example1.ttl') - .expect('content-type', /text\/turtle/) - .expect(hasHeader('acl', 'example1.ttl' + suffixAcl)) - .expect(hasHeader('describedBy', 'example1.ttl' + suffixMeta)) - .end(done) - }) + it('should have set acl and describedBy Links for resource', + function (done) { + server.get('/sampleContainer2/example1.ttl') + .expect('content-type', /text\/turtle/) + .expect(hasHeader('acl', 'example1.ttl' + suffixAcl)) + .expect(hasHeader('describedBy', 'example1.ttl' + suffixMeta)) + .end(done) + }) + it('should have set storageDescription Link for resource', + function (done) { + server.get('/sampleContainer2/example1.ttl') + .expect('content-type', /text\/turtle/) + .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .end(done) + }) it('should have set Link as Container/BasicContainer', function (done) { server.get('/sampleContainer2/') .expect('content-type', /text\/turtle/) @@ -383,14 +404,21 @@ describe('HTTP APIs', function () { }) .end(done) }) - it('should have set acl and describedBy Links for container', - function (done) { - server.get('/sampleContainer2/') - .expect(hasHeader('acl', suffixAcl)) - .expect(hasHeader('describedBy', suffixMeta)) - .expect('content-type', /text\/turtle/) - .end(done) - }) + it('should have set acl and describedBy Links for container', + function (done) { + server.get('/sampleContainer2/') + .expect(hasHeader('acl', suffixAcl)) + .expect(hasHeader('describedBy', suffixMeta)) + .expect('content-type', /text\/turtle/) + .end(done) + }) + it('should have set storageDescription Link for container', + function (done) { + server.get('/sampleContainer2/') + .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .expect('content-type', /text\/turtle/) + .end(done) + }) it('should return requested index.html resource by default', function (done) { server.get('/sampleContainer/index.html') .set('accept', 'text/html') @@ -510,13 +538,19 @@ describe('HTTP APIs', function () { .expect('Link', /; rel="type"/) .expect(200, done) }) - it('should have set acl and describedBy Links for resource', - function (done) { - server.head('/sampleContainer2/example1.ttl') - .expect(hasHeader('acl', 'example1.ttl' + suffixAcl)) - .expect(hasHeader('describedBy', 'example1.ttl' + suffixMeta)) - .end(done) - }) + it('should have set acl and describedBy Links for resource', + function (done) { + server.head('/sampleContainer2/example1.ttl') + .expect(hasHeader('acl', 'example1.ttl' + suffixAcl)) + .expect(hasHeader('describedBy', 'example1.ttl' + suffixMeta)) + .end(done) + }) + it('should have set storageDescription Link for resource', + function (done) { + server.head('/sampleContainer2/example1.ttl') + .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .end(done) + }) it('should have set Content-Type as text/turtle for Container', function (done) { server.head('/sampleContainer2/') @@ -530,14 +564,20 @@ describe('HTTP APIs', function () { .expect('Link', /; rel="type"/) .expect(200, done) }) - it('should have set acl and describedBy Links for container', - function (done) { - server.head('/sampleContainer2/') - .expect(hasHeader('acl', suffixAcl)) - .expect(hasHeader('describedBy', suffixMeta)) - .end(done) - }) - }) + it('should have set acl and describedBy Links for container', + function (done) { + server.head('/sampleContainer2/') + .expect(hasHeader('acl', suffixAcl)) + .expect(hasHeader('describedBy', suffixMeta)) + .end(done) + }) + it('should have set storageDescription Link for container', + function (done) { + server.head('/sampleContainer2/') + .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .end(done) + }) + }) describe('PUT API', function () { const putRequestBody = fs.readFileSync(path.join(__dirname, From 49b4fb9159c8c6a609f8891b7f5c0794b95be0ac Mon Sep 17 00:00:00 2001 From: SarthakDudhe Date: Wed, 6 May 2026 19:58:14 +0530 Subject: [PATCH 2/2] Test storage description link discovery --- test/integration/http-test.mjs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/integration/http-test.mjs b/test/integration/http-test.mjs index 51d532ad3..b045c6426 100644 --- a/test/integration/http-test.mjs +++ b/test/integration/http-test.mjs @@ -12,6 +12,7 @@ const __dirname = path.dirname(__filename) const suffixAcl = '.acl' const suffixMeta = '.meta' const storageDescription = 'http://www.w3.org/ns/solid/terms#storageDescription' +const storageDescriptionResource = 'https://localhost:8443/' + suffixMeta const server = setupSupertestServer({ live: true, dataBrowserPath: 'default', @@ -174,7 +175,7 @@ describe('HTTP APIs', function () { it('should have set storageDescription Link for resource', function (done) { server.options('/sampleContainer2/example1.ttl') - .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .expect(hasHeader(storageDescription, storageDescriptionResource)) .end(done) }) @@ -215,7 +216,7 @@ describe('HTTP APIs', function () { it('should have set storageDescription Link for container', function (done) { server.options('/sampleContainer2/') - .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .expect(hasHeader(storageDescription, storageDescriptionResource)) .end(done) }) }) @@ -291,7 +292,7 @@ describe('HTTP APIs', function () { function (done) { server.get('/sampleContainer2/example1.ttl') .expect('content-type', /text\/turtle/) - .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .expect(hasHeader(storageDescription, storageDescriptionResource)) .end(done) }) it('should have set Link as Container/BasicContainer', function (done) { @@ -415,7 +416,7 @@ describe('HTTP APIs', function () { it('should have set storageDescription Link for container', function (done) { server.get('/sampleContainer2/') - .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .expect(hasHeader(storageDescription, storageDescriptionResource)) .expect('content-type', /text\/turtle/) .end(done) }) @@ -548,7 +549,7 @@ describe('HTTP APIs', function () { it('should have set storageDescription Link for resource', function (done) { server.head('/sampleContainer2/example1.ttl') - .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .expect(hasHeader(storageDescription, storageDescriptionResource)) .end(done) }) it('should have set Content-Type as text/turtle for Container', @@ -574,7 +575,7 @@ describe('HTTP APIs', function () { it('should have set storageDescription Link for container', function (done) { server.head('/sampleContainer2/') - .expect(hasHeader(storageDescription, 'https://localhost/' + suffixMeta)) + .expect(hasHeader(storageDescription, storageDescriptionResource)) .end(done) }) })