forked from nodeSolidServer/node-solid-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnotify.js
More file actions
130 lines (118 loc) · 4.03 KB
/
Copy pathnotify.js
File metadata and controls
130 lines (118 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
module.exports = handler
const libPath = require('path/posix')
const headerTemplate = require('express-prep/templates').header
const solidRDFTemplate = require('../rdf-notification-template')
const debug = require('../debug').prep
const ALLOWED_RDF_MIME_TYPES = [
'application/ld+json',
'application/activity+json',
'text/turtle'
]
function getParent (path) {
if (path === '' || path === '/') return
const parent = libPath.dirname(path)
return parent === '/' ? '/' : `${parent}/`
}
function getActivity (method, path) {
if (method === 'DELETE') {
return 'Delete'
}
if (method === 'POST' && path.endsWith('/')) {
return 'Add'
}
return 'Update'
}
function getParentActivity (method, status) {
if (method === 'DELETE') {
return 'Remove'
}
if (status === 201) {
return 'Add'
}
return 'Update'
}
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 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()
// If the resource itself newly created,
// it could not have been subscribed for notifications already
if (!((method === 'PUT' || method === 'PATCH') && statusCode === 201)) {
try {
trigger({
generateNotification (
negotiatedFields
) {
const mediaType = negotiatedFields['content-type']
const activity = getActivity(method, path)
const target = activity === 'Add'
? res.getHeader('location')
: undefined
if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
activity,
eventID,
object: String(fullUrl),
target,
date: eventDate,
// We use eTag as a proxy for state for now
state: res.getHeader('ETag'),
mediaType
})}`
} else {
return defaultNotification()
}
}
})
} catch (error) {
debug(`Failed to trigger notification on route ${fullUrl}`)
// No special handling is necessary since the resource mutation was
// already successful. The purpose of this block is to prevent Express
// from triggering error handling middleware when notifications fail.
// An error notification might be sent in the future.
}
}
// Write a notification to parent container
// POST in Solid creates a child resource
const parent = getParent(path)
if (parent && method !== 'POST') {
const parentID = res.setEventID(parent)
const parentUrl = new URL(parent, fullUrl)
try {
trigger({
path: parent,
generateNotification (
negotiatedFields
) {
const mediaType = negotiatedFields['content-type']
const activity = getParentActivity(method, statusCode)
const target = activity !== 'Update' ? String(fullUrl) : undefined
if (ALLOWED_RDF_MIME_TYPES.includes(mediaType?.[0])) {
return `${headerTemplate(negotiatedFields)}\r\n${solidRDFTemplate({
activity,
eventID: parentID,
date: eventDate,
object: String(parentUrl),
target,
eTag: undefined,
mediaType
})}`
}
}
})
} catch (error) {
debug(`Failed to trigger notification on parent route ${parentUrl}`)
// No special handling is necessary since the resource mutation was
// already successful. The purpose of this block is to prevent Express
// from triggering error handling middleware when notifications fail.
// An error notification might be sent in the future.
}
}
next()
}