diff --git a/lib/create-app.js b/lib/create-app.js index b2c36bfc..0cc00b6b 100644 --- a/lib/create-app.js +++ b/lib/create-app.js @@ -32,7 +32,7 @@ const corsSettings = cors({ methods: [ 'OPTIONS', 'HEAD', 'GET', 'PATCH', 'POST', 'PUT', 'DELETE' ], - exposedHeaders: 'Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By', + exposedHeaders: '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', credentials: true, maxAge: 1728000, origin: true, diff --git a/lib/handlers/get.js b/lib/handlers/get.js index a35924ea..b73146ce 100644 --- a/lib/handlers/get.js +++ b/lib/handlers/get.js @@ -27,8 +27,13 @@ async function handler (req, res, next) { const requestedType = negotiator.mediaType() const possibleRDFType = negotiator.mediaType(RDFs) + // deprecated kept for compatibility res.header('MS-Author-Via', 'SPARQL') + res.header('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + res.header('Accept-Post', '*/*') + if (!path.endsWith('/') && !glob.hasMagic(path)) res.header('Accept-Put', '*/*') + // Set live updates if (ldp.live) { res.header('Updates-Via', ldp.resourceMapper.resolveUrl(req.hostname).replace(/^http/, 'ws')) @@ -49,6 +54,8 @@ async function handler (req, res, next) { try { ret = await ldp.get(options, req.accepts(['html', 'turtle', 'rdf+xml', 'n3', 'ld+json']) === 'html') } catch (err) { + // set Accept-Put if container do not exist + if (err.status === 404 && path.endsWith('/')) res.header('Accept-Put', 'text/turtle') // use globHandler if magic is detected if (err.status === 404 && glob.hasMagic(path)) { debug('forwarding to glob request') diff --git a/lib/handlers/options.js b/lib/handlers/options.js index def5dfc4..70d0eca1 100644 --- a/lib/handlers/options.js +++ b/lib/handlers/options.js @@ -8,7 +8,7 @@ module.exports = handler function handler (req, res, next) { linkServiceEndpoint(req, res) linkAuthProvider(req, res) - linkSparqlEndpoint(res) + linkAcceptEndpoint(res) res.status(204) @@ -28,6 +28,8 @@ function linkServiceEndpoint (req, res) { addLink(res, serviceEndpoint, 'service') } -function linkSparqlEndpoint (res) { - res.header('Accept-Patch', 'application/sparql-update') +function linkAcceptEndpoint (res) { + res.header('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + res.header('Accept-Post', '*/*') + res.header('Accept-Put', '*/*') } diff --git a/lib/handlers/patch.js b/lib/handlers/patch.js index da255206..8001c9f5 100644 --- a/lib/handlers/patch.js +++ b/lib/handlers/patch.js @@ -39,7 +39,6 @@ function contentForNew (contentType) { // Handles a PATCH request async function patchHandler (req, res, next) { debug(`PATCH -- ${req.originalUrl}`) - res.header('MS-Author-Via', 'SPARQL') try { // Obtain details of the target resource const ldp = req.app.locals.ldp @@ -77,7 +76,7 @@ async function patchHandler (req, res, next) { if (!parsePatch) { throw error(415, `Unsupported patch content type: ${patch.contentType}`) } - + res.header('Accept-Patch', patch.contentType) // is this needed ? // Parse the patch document and verify permissions const patchObject = await parsePatch(url, patch.uri, patch.text) await checkPermission(req, patchObject, resourceExists) diff --git a/lib/handlers/put.js b/lib/handlers/put.js index b9981019..740bf346 100644 --- a/lib/handlers/put.js +++ b/lib/handlers/put.js @@ -8,7 +8,8 @@ const { stringToStream } = require('../utils') async function handler (req, res, next) { debug(req.originalUrl) - res.header('MS-Author-Via', 'SPARQL') + // deprecated kept for compatibility + res.header('MS-Author-Via', 'SPARQL') // is this needed ? const contentType = req.get('content-type') // check whether a folder or resource with same name exists diff --git a/test/integration/header-test.js b/test/integration/header-test.js index e535d7a1..ca5aff50 100644 --- a/test/integration/header-test.js +++ b/test/integration/header-test.js @@ -20,7 +20,7 @@ describe('Header handler', () => { request = supertest(server) }) - describe('MS-Author-Via', () => { + describe('MS-Author-Via', () => { // deprecated describeHeaderTest('read/append for the public', { resource: '/public-ra', headers: { @@ -30,6 +30,18 @@ describe('Header handler', () => { }) }) + describe('Accept-* for a resource document', () => { + describeHeaderTest('read/append for the public', { + resource: '/public-ra', + headers: { + 'Accept-Patch': 'text/n3, application/sparql-update, application/sparql-update-single-match', + 'Accept-Post': '*/*', + 'Accept-Put': '*/*', + 'Access-Control-Expose-Headers': /(^|,\s*)Accept-Patch, Accept-Post, Accept-Put(,|$)/ + } + }) + }) + describe('WAC-Allow', () => { describeHeaderTest('read/append for the public', { resource: '/public-ra', diff --git a/test/integration/http-test.js b/test/integration/http-test.js index 1a010442..4ef3f094 100644 --- a/test/integration/http-test.js +++ b/test/integration/http-test.js @@ -110,26 +110,32 @@ describe('HTTP APIs', function () { .expect('Access-Control-Allow-Origin', 'http://example.com') .expect('Access-Control-Allow-Credentials', 'true') .expect('Access-Control-Allow-Methods', 'OPTIONS,HEAD,GET,PATCH,POST,PUT,DELETE') - .expect('Access-Control-Expose-Headers', 'Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By') + .expect('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') .expect(204, done) }) - describe('Accept-Patch header', function () { + describe('Accept-* headers', function () { it('should be present for resources', function (done) { server.options('/sampleContainer/example1.ttl') - .expect('Accept-Patch', 'application/sparql-update') + .expect('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + .expect('Accept-Post', '*/*') + .expect('Accept-Put', '*/*') .expect(204, done) }) it('should be present for containers', function (done) { server.options('/sampleContainer/') - .expect('Accept-Patch', 'application/sparql-update') + .expect('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + .expect('Accept-Post', '*/*') + .expect('Accept-Put', '*/*') .expect(204, done) }) it('should be present for non-rdf resources', function (done) { server.options('/sampleContainer/solid.png') - .expect('Accept-Patch', 'application/sparql-update') + .expect('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + .expect('Accept-Post', '*/*') + .expect('Accept-Put', '*/*') .expect(204, done) }) }) @@ -329,6 +335,11 @@ describe('HTTP APIs', function () { server.get('/invalidfile.foo') .expect(404, done) }) + it('should return 404 for non-existent container', function (done) { // alain + server.get('/inexistant/') + .expect('Accept-Put', 'text/turtle') + .expect(404, done) + }) it('should return basic container link for directories', function (done) { server.get('/') .expect('Link', /http:\/\/www.w3.org\/ns\/ldp#BasicContainer/) @@ -411,13 +422,39 @@ describe('HTTP APIs', function () { .expect('content-type', /text\/turtle/) .end(done) }) - it('should still redirect to the right container URI if missing / and HTML is requested', - function (done) { - server.get('/sampleContainer') - .set('accept', 'text/html') - .expect('location', /\/sampleContainer\//) - .expect(301, done) + it('should still redirect to the right container URI if missing / and HTML is requested', function (done) { + server.get('/sampleContainer') + .set('accept', 'text/html') + .expect('location', /\/sampleContainer\//) + .expect(301, done) + }) + + describe('Accept-* headers', function () { + it('should return 404 for non-existent resource', function (done) { + server.get('/invalidfile.foo') + .expect('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + .expect('Accept-Post', '*/*') + .expect('Accept-put', '*/*') + .expect(404, done) + }) + it('Accept-Put=text/turtle for non-existent container', function (done) { + server.get('/inexistant/') + .expect('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + .expect('Accept-Post', '*/*') + .expect('Accept-Put', 'text/turtle') + .expect(404, done) }) + it('Accept-Put header do not exist for existing container', (done) => { + server.get('/sampleContainer/') + .expect(200) + .expect('Accept-Patch', 'text/n3, application/sparql-update, application/sparql-update-single-match') + .expect('Accept-Post', '*/*') + .expect((res) => { + if (res.headers['Accept-Put']) return done(new Error('Accept-Put header should not exist')) + }) + .end(done) + }) + }) }) describe('HEAD API', function () { diff --git a/test/integration/patch-test.js b/test/integration/patch-test.js index 34f9d351..53ddb90e 100644 --- a/test/integration/patch-test.js +++ b/test/integration/patch-test.js @@ -22,7 +22,7 @@ const serverOptions = { forceUser: `${serverUri}/profile/card#me` } -describe('PATCH', () => { +describe('PATCH through text/n3', () => { let request let server