Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic prep fix 1 #1801

Merged
merged 6 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading