Skip to content

Commit

Permalink
Merge pull request #1801 from nodeSolidServer/basic-prep-fix-1
Browse files Browse the repository at this point in the history
Basic prep fix 1
  • Loading branch information
bourgeoa authored Nov 23, 2024
2 parents 41a2ba3 + d86d150 commit 96ce2f0
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

strategy:
matrix:
node-version: [ '>=20.17.0' ]
node-version: [ '^20.17.0' ]
os: [ubuntu-latest]

steps:
Expand Down
3 changes: 0 additions & 3 deletions lib/handlers/delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ async function handler (req, res, next) {
debug('DELETE -- Request on' + req.originalUrl)

const ldp = req.app.locals.ldp
const prep = req.app.locals.prep
try {
await ldp.delete(req)
debug('DELETE -- Ok.')
// Add event-id for notifications
prep && res.setHeader('Event-ID', res.setEventID())
res.sendStatus(200)
next()
} catch (err) {
Expand Down
42 changes: 30 additions & 12 deletions lib/handlers/notify.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,32 @@ function getParentActivity (method, status) {
return 'Update'
}

function filterMillseconds (isoDate) {
return `${isoDate.substring(0, 19)}${isoDate.substring(23)}`
}

function getDate (date) {
if (date) {
const eventDate = new Date(date)
if (!isNaN(eventDate.valueOf())) {
return filterMillseconds(eventDate.toISOString())
}
}
const now = new Date()
return filterMillseconds(now.toISOString())
}

function handler (req, res, next) {
const { trigger, defaultNotification } = res.events.prep

const { method, path } = req
const { statusCode } = res
const eventID = res.getHeader('event-id')
const eventID = res.setEventID()
const fullUrl = new URL(path, `${req.protocol}://${req.hostname}/`)

// Date is a hack since node does not seem to provide access to send date.
// Date needs to be shared with parent notification
const eventDate = res._header.match(/^Date: (.*?)$/m)?.[1] ||
new Date().toUTCString()
const eventDate = getDate(res._header.match(/^Date: (.*?)$/m)?.[1])

// If the resource itself newly created,
// it could not have been subscribed for notifications already
Expand All @@ -61,18 +75,19 @@ function handler (req, res, next) {
) {
const mediaType = negotiatedFields['content-type']
const activity = getActivity(method, path)
const target = activity === 'Add'
const object = activity === 'Add'
? res.getHeader('location')
: String(fullUrl)
const target = activity === 'Add'
? String(fullUrl)
: undefined
if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
activity,
eventID,
object: String(fullUrl),
object,
target,
date: eventDate,
// We use eTag as a proxy for state for now
state: res.getHeader('ETag'),
mediaType
})}`
} else {
Expand All @@ -93,7 +108,10 @@ function handler (req, res, next) {
// POST in Solid creates a child resource
const parent = getParent(path)
if (parent && method !== 'POST') {
const parentID = res.setEventID(parent)
res.setEventID({
path: parent,
id: eventID
})
const parentUrl = new URL(parent, fullUrl)
try {
trigger({
Expand All @@ -103,15 +121,15 @@ function handler (req, res, next) {
) {
const mediaType = negotiatedFields['content-type']
const activity = getParentActivity(method, statusCode)
const target = activity !== 'Update' ? String(fullUrl) : undefined
const object = activity === 'Update' ? String(parentUrl) : String(fullUrl)
const target = activity === 'Update' ? undefined : String(parentUrl)
if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
activity,
eventID: parentID,
eventID,
date: eventDate,
object: String(parentUrl),
object,
target,
eTag: undefined,
mediaType
})}`
}
Expand Down
4 changes: 0 additions & 4 deletions lib/handlers/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ function contentForNew (contentType) {
// Handles a PATCH request
async function patchHandler (req, res, next) {
debug(`PATCH -- ${req.originalUrl}`)
const prep = req.app.locals.prep
try {
// Obtain details of the target resource
const ldp = req.app.locals.ldp
Expand Down Expand Up @@ -91,9 +90,6 @@ async function patchHandler (req, res, next) {
await applyPatch(patchObject, graph, url)
return writeGraph(graph, resource, ldp.resourceMapper.resolveFilePath(req.hostname), ldp.serverUri)
})

// Add event-id for notifications
prep && res.setHeader('Event-ID', res.setEventID())
// Send the status and result to the client
res.status(resourceExists ? 200 : 201)
res.send(result)
Expand Down
5 changes: 0 additions & 5 deletions lib/handlers/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const getContentType = require('../utils').getContentType

async function handler (req, res, next) {
const ldp = req.app.locals.ldp
const prep = req.app.locals.prep
const contentType = getContentType(req.headers)
debug('content-type is ', contentType)
// Handle SPARQL(-update?) query
Expand Down Expand Up @@ -73,8 +72,6 @@ async function handler (req, res, next) {
// Handled by backpressure of streams!
busboy.on('finish', function () {
debug('Done storing files')
// Add event-id for notifications
prep && res.setHeader('Event-ID', res.setEventID())
res.sendStatus(200)
next()
})
Expand All @@ -94,8 +91,6 @@ async function handler (req, res, next) {
debug('File stored in ' + resourcePath)
header.addLinks(res, links)
res.set('Location', resourcePath)
// Add event-id for notifications
prep && res.setHeader('Event-ID', res.setEventID())
res.sendStatus(201)
next()
},
Expand Down
3 changes: 0 additions & 3 deletions lib/handlers/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ async function checkPermission (request, resourceExists) {
// TODO could be renamed as putResource (it now covers container and non-container)
async function putStream (req, res, next, stream = req) {
const ldp = req.app.locals.ldp
const prep = req.app.locals.prep
// try {
// Obtain details of the target resource
let resourceExists = true
Expand All @@ -78,8 +77,6 @@ async function putStream (req, res, next, stream = req) {
// Fails with Append on existing resource
if (!req.originalUrl.endsWith('.acl')) await checkPermission(req, resourceExists)
await ldp.put(req, stream, getContentType(req.headers))
// Add event-id for notifications
prep && res.setHeader('Event-ID', res.setEventID())
res.sendStatus(resourceExists ? 204 : 201)
return next()
} catch (err) {
Expand Down
32 changes: 22 additions & 10 deletions lib/rdf-notification-template.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const uuid = require('uuid')

const CONTEXT_ACTIVITYSTREAMS = 'https://www.w3.org/ns/activitystreams'
const CONTEXT_NOTIFICATION = 'https://www.w3.org/ns/solid/notification/v1'
const CONTEXT_XML_SCHEMA = 'http://www.w3.org/2001/XMLSchema'

function generateJSONNotification ({
activity: type,
eventId: id,
eventID,
date: published,
object,
target,
Expand All @@ -13,30 +15,40 @@ function generateJSONNotification ({
return {
published,
type,
id,
id: `urn:uuid:${uuid.v4()}`,
...(eventID) && { state: eventID },
object,
...(type === 'Add') && { target },
...(type === 'Remove') && { origin: target },
...(state) && { state }
...(type === 'Remove') && { origin: target }
}
}

function generateTurtleNotification ({
activity,
eventId,
eventID,
date,
object,
target,
state = undefined
target
}) {
const stateLine = `\n notify:state "${state}" ;`
let targetLine = ''
let stateLine = ''

if (activity === 'Add') {
targetLine = `\n as:target <${target}> ;`
}
if (activity === 'Remove') {
targetLine = `\n as:origin <${target}> ;`
}
if (eventID) {
stateLine = `\n notify:state "${eventID}" ;`
}

return `@prefix as: <${CONTEXT_ACTIVITYSTREAMS}#> .
@prefix notify: <${CONTEXT_NOTIFICATION}#> .
@prefix xsd: <${CONTEXT_XML_SCHEMA}#> .
<${eventId}> a as:${activity} ;${state && stateLine}
as:object ${object} ;
<urn:uuid:${uuid.v4()}> a as:${activity} ;
as:object <${object}> ;${targetLine}${stateLine}
as:published "${date}"^^xsd:dateTime .`.replaceAll('\n', '\r\n')
}

Expand Down
Loading

0 comments on commit 96ce2f0

Please sign in to comment.