Skip to content

Commit 443eda4

Browse files
committed
Use ResourceMapper in patch handler
1 parent 1e59cba commit 443eda4

File tree

2 files changed

+53
-50
lines changed

2 files changed

+53
-50
lines changed

lib/handlers/patch.js

Lines changed: 45 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,60 @@
33
module.exports = handler
44

55
const bodyParser = require('body-parser')
6-
const mime = require('mime-types')
76
const fs = require('fs')
87
const debug = require('../debug').handlers
9-
const utils = require('../utils.js')
108
const error = require('../http-error')
119
const $rdf = require('rdflib')
1210
const crypto = require('crypto')
1311
const overQuota = require('../utils').overQuota
1412

15-
const DEFAULT_TARGET_TYPE = 'text/turtle'
16-
1713
// Patch parsers by request body content type
1814
const PATCH_PARSERS = {
1915
'application/sparql-update': require('./patch/sparql-update-parser.js'),
2016
'text/n3': require('./patch/n3-patch-parser.js')
2117
}
2218

2319
// Handles a PATCH request
24-
function patchHandler (req, res, next) {
20+
async function patchHandler (req, res, next) {
2521
debug(`PATCH -- ${req.originalUrl}`)
2622
res.header('MS-Author-Via', 'SPARQL')
23+
try {
24+
// Obtain details of the target resource
25+
const ldp = req.app.locals.ldp
26+
const { path, contentType } = await ldp.resourceMapper.mapUrlToFile({ url: req })
27+
const { url } = await ldp.resourceMapper.mapFileToUrl({ path, hostname: req.hostname })
28+
const resource = { path, contentType, url }
29+
debug('PATCH -- Target <%s> (%s)', url, contentType)
30+
31+
// Obtain details of the patch document
32+
const patch = {}
33+
patch.text = req.body ? req.body.toString() : ''
34+
patch.uri = `${url}#patch-${hash(patch.text)}`
35+
patch.contentType = (req.get('content-type') || '').match(/^[^;\s]*/)[0]
36+
debug('PATCH -- Received patch (%d bytes, %s)', patch.text.length, patch.contentType)
37+
const parsePatch = PATCH_PARSERS[patch.contentType]
38+
if (!parsePatch) {
39+
throw error(415, `Unsupported patch content type: ${patch.contentType}`)
40+
}
2741

28-
// Obtain details of the target resource
29-
const ldp = req.app.locals.ldp
30-
const root = !ldp.multiuser ? ldp.root : `${ldp.root}${req.hostname}/`
31-
const target = {}
32-
target.file = utils.uriToFilename(req.path, root)
33-
target.uri = utils.getBaseUri(req) + req.originalUrl
34-
target.contentType = mime.lookup(target.file) || DEFAULT_TARGET_TYPE
35-
debug('PATCH -- Target <%s> (%s)', target.uri, target.contentType)
36-
37-
// Obtain details of the patch document
38-
const patch = {}
39-
patch.text = req.body ? req.body.toString() : ''
40-
patch.uri = `${target.uri}#patch-${hash(patch.text)}`
41-
patch.contentType = (req.get('content-type') || '').match(/^[^;\s]*/)[0]
42-
debug('PATCH -- Received patch (%d bytes, %s)', patch.text.length, patch.contentType)
43-
const parsePatch = PATCH_PARSERS[patch.contentType]
44-
if (!parsePatch) {
45-
return next(error(415, `Unsupported patch content type: ${patch.contentType}`))
42+
// Parse the target graph and the patch document,
43+
// and verify permission for performing this specific patch
44+
const [graph, patchObject] = await Promise.all([
45+
readGraph(resource),
46+
parsePatch(url, patch.uri, patch.text)
47+
.then(patchObject => checkPermission(req, patchObject))
48+
])
49+
50+
// Patch the graph and write it back to the file
51+
await applyPatch(patchObject, graph, url)
52+
const result = await writeGraph(graph, resource, ldp.resourceMapper.rootPath, ldp.serverUri)
53+
54+
// Send the result to the client
55+
res.send(result)
56+
} catch (err) {
57+
return next(err)
4658
}
47-
48-
// Parse the target graph and the patch document,
49-
// and verify permission for performing this specific patch
50-
Promise.all([
51-
readGraph(target),
52-
parsePatch(target.uri, patch.uri, patch.text)
53-
.then(patchObject => checkPermission(target, req, patchObject))
54-
])
55-
// Patch the graph and write it back to the file
56-
.then(([graph, patchObject]) => applyPatch(patchObject, graph, target))
57-
.then(graph => writeGraph(graph, target, root, ldp.serverUri))
58-
// Send the result to the client
59-
.then(result => { res.send(result) })
60-
.then(next, next)
59+
return next()
6160
}
6261

6362
// Reads the request body and calls the actual patch handler
@@ -70,7 +69,7 @@ const readEntity = bodyParser.text({ type: () => true })
7069
function readGraph (resource) {
7170
// Read the resource's file
7271
return new Promise((resolve, reject) =>
73-
fs.readFile(resource.file, {encoding: 'utf8'}, function (err, fileContents) {
72+
fs.readFile(resource.path, {encoding: 'utf8'}, function (err, fileContents) {
7473
if (err) {
7574
// If the file does not exist, assume empty contents
7675
// (it will be created after a successful patch)
@@ -88,9 +87,9 @@ function readGraph (resource) {
8887
// Parse the resource's file contents
8988
.then((fileContents) => {
9089
const graph = $rdf.graph()
91-
debug('PATCH -- Reading %s with content type %s', resource.uri, resource.contentType)
90+
debug('PATCH -- Reading %s with content type %s', resource.url, resource.contentType)
9291
try {
93-
$rdf.parse(fileContents, graph, resource.uri, resource.contentType)
92+
$rdf.parse(fileContents, graph, resource.url, resource.contentType)
9493
} catch (err) {
9594
throw error(500, `Patch: Target ${resource.contentType} file syntax error: ${err}`)
9695
}
@@ -100,7 +99,7 @@ function readGraph (resource) {
10099
}
101100

102101
// Verifies whether the user is allowed to perform the patch on the target
103-
function checkPermission (target, request, patchObject) {
102+
function checkPermission (request, patchObject) {
104103
// If no ACL object was passed down, assume permissions are okay.
105104
if (!request.acl) return Promise.resolve(patchObject)
106105
// At this point, we already assume append access,
@@ -123,10 +122,10 @@ function checkPermission (target, request, patchObject) {
123122
}
124123

125124
// Applies the patch to the RDF graph
126-
function applyPatch (patchObject, graph, target) {
125+
function applyPatch (patchObject, graph, url) {
127126
debug('PATCH -- Applying patch')
128127
return new Promise((resolve, reject) =>
129-
graph.applyPatch(patchObject, graph.sym(target.uri), (err) => {
128+
graph.applyPatch(patchObject, graph.sym(url), (err) => {
130129
if (err) {
131130
const message = err.message || err // returns string at the moment
132131
debug(`PATCH -- FAILED. Returning 409. Message: '${message}'`)
@@ -141,8 +140,8 @@ function applyPatch (patchObject, graph, target) {
141140
function writeGraph (graph, resource, root, serverUri) {
142141
debug('PATCH -- Writing patched file')
143142
return new Promise((resolve, reject) => {
144-
const resourceSym = graph.sym(resource.uri)
145-
const serialized = $rdf.serialize(resourceSym, graph, resource.uri, resource.contentType)
143+
const resourceSym = graph.sym(resource.url)
144+
const serialized = $rdf.serialize(resourceSym, graph, resource.url, resource.contentType)
146145

147146
// First check if we are above quota
148147
overQuota(root, serverUri).then((isOverQuota) => {
@@ -151,7 +150,7 @@ function writeGraph (graph, resource, root, serverUri) {
151150
'User has exceeded their storage quota'))
152151
}
153152

154-
fs.writeFile(resource.file, serialized, {encoding: 'utf8'}, function (err) {
153+
fs.writeFile(resource.path, serialized, {encoding: 'utf8'}, function (err) {
155154
if (err) {
156155
return reject(error(500, `Failed to write file after patch: ${err}`))
157156
}

test/integration/ldp-test.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,14 @@ describe('LDP', function () {
212212

213213
after(async function () {
214214
// Clean up after delete tests
215-
await ldp.delete('/resources/dummy/testPutBlocking.txt')
216-
await ldp.delete('/resources/dummy/dummy2/testPutBlocking.txt')
217-
await ldp.delete('/resources/dummy/dummy2/')
218-
await ldp.delete('/resources/dummy/')
215+
try {
216+
await ldp.delete('/resources/dummy/testPutBlocking.txt')
217+
await ldp.delete('/resources/dummy/dummy2/testPutBlocking.txt')
218+
await ldp.delete('/resources/dummy/dummy2/')
219+
await ldp.delete('/resources/dummy/')
220+
} catch (err) {
221+
222+
}
219223
})
220224
})
221225
describe('listContainer', function () {

0 commit comments

Comments
 (0)