33module . exports = handler
44
55const bodyParser = require ( 'body-parser' )
6- const mime = require ( 'mime-types' )
76const fs = require ( 'fs' )
87const debug = require ( '../debug' ) . handlers
9- const utils = require ( '../utils.js' )
108const error = require ( '../http-error' )
119const $rdf = require ( 'rdflib' )
1210const crypto = require ( 'crypto' )
1311const overQuota = require ( '../utils' ) . overQuota
1412
15- const DEFAULT_TARGET_TYPE = 'text/turtle'
16-
1713// Patch parsers by request body content type
1814const 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 })
7069function 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) {
141140function 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 }
0 commit comments