ldn-inbox-server 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -7
- package/bin/ldn-inbox-server.js +1 -1
- package/bin/post-notification.js +24 -0
- package/config/inbox_config.json +23 -2
- package/config/outbox_config.json +5 -0
- package/examples/offer.jsonld +2 -2
- package/handler/{demo_notification_handler.js → accept_notification_handler.js} +7 -1
- package/handler/eventlog_notification_handler.js +91 -19
- package/handler/multi_notification_handler.js +4 -0
- package/handler/offer_memento_notification_handler.js +68 -0
- package/handler/send_notification_handler.js +6 -4
- package/handler/type_filter_notification_handler.js +65 -0
- package/handler/valid_artifact_notification_handler.js +55 -0
- package/lib/util.js +58 -2
- package/package.json +4 -3
- package/public/artifacts-example/artifact1.html +17 -0
- package/public/artifacts-example/artifact1.html.meta +6 -0
- package/public/artifacts-example/artifact2.html +17 -0
- package/public/artifacts-example/artifact2.html.meta +6 -0
- package/public/index.html.meta +3 -0
package/README.md
CHANGED
|
@@ -33,13 +33,13 @@ curl -X POST -H 'Content-Type: application/ld+json' --data-binary '@examples/off
|
|
|
33
33
|
Start an inbox handler with a demo handler (that creates an `Accept` message in the `./outbox`).
|
|
34
34
|
|
|
35
35
|
```
|
|
36
|
-
npx ldn-inbox-server
|
|
36
|
+
npx ldn-inbox-server handler @inbox -hn ./handler/accept_notification_handler.js
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Start an outbox handler that send the notifications that are available outbox:
|
|
40
40
|
|
|
41
41
|
```
|
|
42
|
-
npx ldn-inbox-server
|
|
42
|
+
npx ldn-inbox-server handler @outbox -hn ./handler/send_notification_handler.js
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Environment
|
|
@@ -62,7 +62,7 @@ npx ldn-inbox-server handle @outbox -hn ./handler/send_notification_handler.js
|
|
|
62
62
|
Server extensions are possible by providing custom inbox and notification handlers. E.g.
|
|
63
63
|
|
|
64
64
|
```
|
|
65
|
-
npx ldn-inbox-server
|
|
65
|
+
npx ldn-inbox-server handler @inbox -hn handler/my_handler.js
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
Or, in JavaScript:
|
|
@@ -81,12 +81,12 @@ async function main() {
|
|
|
81
81
|
'batch_size': 5,
|
|
82
82
|
'glob': '^.*\\.jsonld$',
|
|
83
83
|
'config': './config/inbox_config.json',
|
|
84
|
-
'notification_handler': 'handler/
|
|
84
|
+
'notification_handler': 'handler/my_handler.js'
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
with `
|
|
89
|
+
with `my_handler.js` :
|
|
90
90
|
|
|
91
91
|
```
|
|
92
92
|
async function handle({path,options}) {
|
|
@@ -109,10 +109,54 @@ A handler can be started on any directory. E.g. a workflow might be:
|
|
|
109
109
|
- processed LDN messages will end up in the "outbox" box
|
|
110
110
|
- invalid processing will be saved into the "error" box
|
|
111
111
|
|
|
112
|
-
##
|
|
112
|
+
## Handlers
|
|
113
|
+
|
|
114
|
+
### Accept handler
|
|
115
|
+
|
|
116
|
+
A handler that creates for an incoming notification an `Accept` notification in the `@outbox`.
|
|
117
|
+
|
|
118
|
+
### Evenlog handler
|
|
119
|
+
|
|
120
|
+
A handler that updates an event log with the incoming notification.
|
|
121
|
+
|
|
122
|
+
Requires a configuration file with property `notification_handler.eventlog`:
|
|
123
|
+
|
|
124
|
+
- `log`: the path to the event log (starting from the `public` directory)
|
|
125
|
+
- `dir`: the path to a container to store the events (starting from the `public` directory)
|
|
126
|
+
|
|
127
|
+
The `log` and `dir` path may contain a `@artifact(:strip)?@` directive to fill in the
|
|
128
|
+
path of the current artifact. The `:strip` filter is used to strip an artifact path of a file extension. E.g. `path/artifact.html` becomes `path/artifact` when using a `:strip`.
|
|
129
|
+
|
|
130
|
+
### Multi handler
|
|
113
131
|
|
|
114
132
|
A `handler/multi_notification_handler.js` is available to start multiple handler for each notification messages. The handlers to start are specified in a configuraton file that can be passed via the `config` parameter of an `handle_inbox`. In the commmand line tool `bin/ldn-inbox-server` the default location of such config file is `config/inbox_config.json` when processing an `@inbox`, and `config/outbox_config.json` when processing an `@outbox`.
|
|
115
133
|
|
|
134
|
+
### Offer memento handler
|
|
135
|
+
|
|
136
|
+
A handler to `Offer` an event log to a memento server.
|
|
137
|
+
|
|
138
|
+
Requires a configuration file with property `notification_handler.offer_memento`:
|
|
139
|
+
|
|
140
|
+
- `actor`: the LDN+AS2 `actor` to use in a notification
|
|
141
|
+
- `target`: the LDN+AS2 `target` to use in a notification
|
|
142
|
+
|
|
143
|
+
### Send handler
|
|
144
|
+
|
|
145
|
+
A hanlder to send notification that live in the `@outbox` via the LDN protocol to the LDN `target`.
|
|
146
|
+
|
|
147
|
+
### Valid artifact
|
|
148
|
+
|
|
149
|
+
A handler that validates the incoming notification and checks if the `object` or `context` contains an artifact that is part of the `public` resources. See 'Artifact support' below.
|
|
150
|
+
|
|
151
|
+
## Artifact support
|
|
152
|
+
|
|
153
|
+
This code base contains Event Notifications support for Data Node artifacts. See the examples
|
|
154
|
+
in `public/artifacts-example`. Move this directory tp `public/artifacts` to get a running example.
|
|
155
|
+
|
|
156
|
+
- Each artifact requires at least a `.meta` file with the `X-Artifact` header set to `true` to be recognized by the software as an artifact
|
|
157
|
+
- Each artifact should update the `Link-Template` header in the `.meta` file
|
|
158
|
+
- The config files in `config/inbox_config.json` and `config/outbox_config.json` define the location of the possible event logs for the artifact
|
|
159
|
+
|
|
116
160
|
## See also
|
|
117
161
|
|
|
118
162
|
- [mellon-server](https://www.npmjs.com/package/mellon-server)
|
package/bin/ldn-inbox-server.js
CHANGED
|
@@ -23,7 +23,6 @@ const HAS_PUBLIC = process.env.LDN_SERVER_HAS_PUBLIC_INBOX ?? 0;
|
|
|
23
23
|
|
|
24
24
|
program
|
|
25
25
|
.name('lnd-inbox-server')
|
|
26
|
-
.version('1.2.2')
|
|
27
26
|
.description('A demonstration Event Notifications Inbox server');
|
|
28
27
|
|
|
29
28
|
program
|
|
@@ -43,6 +42,7 @@ program
|
|
|
43
42
|
|
|
44
43
|
program
|
|
45
44
|
.command('handler')
|
|
45
|
+
.option('--base <url>','base url',INBOX_BASE_URL)
|
|
46
46
|
.option('--inbox <inbox>','inbox',INBOX_PATH)
|
|
47
47
|
.option('--outbox <outbox>','outbox',OUTBOX_PATH)
|
|
48
48
|
.option('--public <public>','public',PUBLIC_PATH)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { program } = require('commander');
|
|
5
|
+
const { sendNotification, getLogger } = require('../lib/util.js');
|
|
6
|
+
require('dotenv').config();
|
|
7
|
+
|
|
8
|
+
const logger = getLogger();
|
|
9
|
+
|
|
10
|
+
const INBOX_URL = process.env.LDN_SERVER_INBOX_URL ?? 'inbox/';
|
|
11
|
+
const INBOX_BASE_URL = process.env.LDN_SERVER_BASEURL ?? 'http://localhost:8000';
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name('post-notification')
|
|
15
|
+
.description('A LDN notification sender')
|
|
16
|
+
.argument('<url>','notification')
|
|
17
|
+
.argument('<file>','notification')
|
|
18
|
+
.action( async(url,file) => {
|
|
19
|
+
const to = url === '@me' ? `${INBOX_BASE_URL}/${INBOX_URL}` : url;
|
|
20
|
+
const json = JSON.parse(fs.readFileSync(file, { encoding: 'utf-8'}));
|
|
21
|
+
await sendNotification(to,json);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
program.parse();
|
package/config/inbox_config.json
CHANGED
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"notification_handler": {
|
|
3
|
+
"eventlog": {
|
|
4
|
+
"log": "@artifact:strip@.jsonld",
|
|
5
|
+
"dir": "@artifact:strip@"
|
|
6
|
+
},
|
|
3
7
|
"multi": {
|
|
4
8
|
"handlers": [
|
|
5
|
-
"handler/
|
|
6
|
-
"handler/
|
|
9
|
+
"handler/type_filter_notification_handler.js",
|
|
10
|
+
"handler/valid_artifact_notification_handler.js" ,
|
|
11
|
+
"handler/eventlog_notification_handler.js",
|
|
12
|
+
"handler/offer_memento_notification_handler.js"
|
|
7
13
|
]
|
|
14
|
+
},
|
|
15
|
+
"offer_memento": {
|
|
16
|
+
"actor": {
|
|
17
|
+
"id": "http://localhost:8000/profile/card#me" ,
|
|
18
|
+
"inbox": "http://localhost:8000/inbox/" ,
|
|
19
|
+
"type": "Service"
|
|
20
|
+
},
|
|
21
|
+
"target": {
|
|
22
|
+
"id": "http://localhost:8001/profile/card#me" ,
|
|
23
|
+
"inbox": "http://localhost:8001/inbox/" ,
|
|
24
|
+
"type": "Service"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"type_filter": {
|
|
28
|
+
"anyOf": [ "Offer" ]
|
|
8
29
|
}
|
|
9
30
|
}
|
|
10
31
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"notification_handler": {
|
|
3
|
+
"eventlog": {
|
|
4
|
+
"log": "@artifact:strip@.jsonld",
|
|
5
|
+
"dir": "@artifact:strip@"
|
|
6
|
+
},
|
|
3
7
|
"multi": {
|
|
4
8
|
"handlers": [
|
|
9
|
+
"handler/valid_artifact_notification_handler.js" ,
|
|
5
10
|
"handler/send_notification_handler.js",
|
|
6
11
|
"handler/eventlog_notification_handler.js"
|
|
7
12
|
]
|
package/examples/offer.jsonld
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
"type": "Offer",
|
|
8
8
|
"actor": {
|
|
9
9
|
"id": "https://acme.org.xyz/profile/card#us",
|
|
10
|
-
"inbox": "
|
|
10
|
+
"inbox": "http://localhost:8000/inbox/",
|
|
11
11
|
"type": "Organization"
|
|
12
12
|
},
|
|
13
13
|
"object": {
|
|
14
|
-
"id": "http://
|
|
14
|
+
"id": "http://localhost:8000/artifacts/artifact1.html",
|
|
15
15
|
"type": [ "Document" , "schema:Dataset" ]
|
|
16
16
|
},
|
|
17
17
|
"target": {
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const md5 = require('md5');
|
|
3
|
+
const { parseAsJSON } = require('../lib/util');
|
|
3
4
|
const logger = require('../lib/util.js').getLogger();
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Demonstration notification handler, that creates an 'Accept'
|
|
8
|
+
* notification for each incoming notification message and stores
|
|
9
|
+
* it in the outbox container.
|
|
10
|
+
*/
|
|
5
11
|
async function handle({path,options}) {
|
|
6
12
|
logger.info(`parsing notification ${path}`);
|
|
7
13
|
|
|
8
14
|
try {
|
|
9
|
-
const json =
|
|
15
|
+
const json = parseAsJSON(path);
|
|
10
16
|
|
|
11
17
|
const id = json['id'];
|
|
12
18
|
const object = json['object'];
|
|
@@ -2,37 +2,112 @@ const fs = require('fs');
|
|
|
2
2
|
const md5 = require('md5');
|
|
3
3
|
const fsPath = require('path');
|
|
4
4
|
const lockfile = require('proper-lockfile');
|
|
5
|
+
const { parseAsJSON } = require('../lib/util');
|
|
5
6
|
const logger = require('../lib/util.js').getLogger();
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Demonstration event log handler
|
|
10
|
+
*/
|
|
10
11
|
async function handle({path,options}) {
|
|
11
12
|
logger.info(`parsing notification ${path}`);
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
const config = parseAsJSON(options['config']);
|
|
15
|
+
|
|
16
|
+
if (! config) {
|
|
17
|
+
logger.error('no configuration found for eventlog_notification_handler');
|
|
18
|
+
return { path, options, success: false };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const thisConfig = config['notification_handler']?.['eventlog'];
|
|
22
|
+
|
|
23
|
+
if (! thisConfig || !thisConfig['log'] || !thisConfig['dir']) {
|
|
24
|
+
logger.error('no log/dir entry for notification_handler.eventlog configuration');
|
|
25
|
+
return { path, options, success: false };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const base = options['base'] ? options['base'] :
|
|
29
|
+
options['host'] && options['port'] ?
|
|
30
|
+
`http://${options['host']}:${options['port']}` :
|
|
31
|
+
'http://localhost:8000';
|
|
32
|
+
|
|
33
|
+
let eventLog = thisConfig['log'];
|
|
34
|
+
let eventDir = thisConfig['dir'];
|
|
35
|
+
|
|
36
|
+
let artifactPath = undefined;
|
|
37
|
+
|
|
38
|
+
if (options['artifact']) {
|
|
39
|
+
artifactPath = options['artifact']['id'].substring(base.length+1);
|
|
40
|
+
logger.info(artifactPath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (eventLog.match(/@artifact(:[a-zA-Z]+)?@/)) {
|
|
44
|
+
if (options['artifact']) {
|
|
45
|
+
if (eventLog.match(/@artifact:strip@/)) {
|
|
46
|
+
eventLog = eventLog.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath.replaceAll(/\.\w+$/g,''));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
eventLog = eventLog.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
logger.error(`requested to parse ${eventLog} but no artifact information available`);
|
|
54
|
+
return { path, options, success: false };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (eventDir.match(/@artifact(:[a-zA-Z]+)?@/)) {
|
|
59
|
+
if (options['artifact']) {
|
|
60
|
+
if (eventDir.match(/@artifact:strip@/)) {
|
|
61
|
+
eventDir = eventDir.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath.replaceAll(/\.\w+$/g,''));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
eventDir = eventDir.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
logger.error(`requested to parse ${eventDir} but no artifact information available`);
|
|
69
|
+
return { path, options, success: false };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
13
73
|
try {
|
|
14
|
-
const json = fs.readFileSync(path, { encoding: 'utf-8'});
|
|
74
|
+
const json = fs.readFileSync(path, { encoding: 'utf-8' });
|
|
15
75
|
|
|
16
76
|
const fileName = path.split('/').pop();
|
|
17
|
-
const logDir = fsPath.join(options['public'],EVENT_DIR,'log');
|
|
18
77
|
|
|
19
|
-
if (! fs.existsSync(
|
|
20
|
-
logger.info(`creating ${
|
|
21
|
-
fs.mkdirSync(
|
|
78
|
+
if (! fs.existsSync(`${options['public']}/${eventDir}`)) {
|
|
79
|
+
logger.info(`creating ${options['public']}/${eventDir}`);
|
|
80
|
+
fs.mkdirSync(`${options['public']}/${eventDir}`, { recursive : true });
|
|
22
81
|
}
|
|
23
|
-
|
|
24
|
-
const outboxFile = fsPath.join(logDir,fileName);
|
|
25
82
|
|
|
26
|
-
|
|
83
|
+
const eventFile = `${options['public']}/${eventDir}/${fileName}`;
|
|
84
|
+
|
|
85
|
+
fs.writeFileSync(eventFile, json);
|
|
27
86
|
|
|
28
87
|
// Updating metadata file
|
|
29
|
-
const metaFile =
|
|
88
|
+
const metaFile = `${options['public']}/${eventLog}.meta`;
|
|
30
89
|
|
|
31
90
|
fs.writeFileSync(metaFile, JSON.stringify({
|
|
32
91
|
'Content-Type': 'application/ld+json',
|
|
33
92
|
'Last-Modified': nowISO()
|
|
34
93
|
},null,4));
|
|
35
94
|
|
|
95
|
+
// Store the path in the options .. yeah yeah we know ugly but it works for now
|
|
96
|
+
|
|
97
|
+
const eventPath = `${eventDir}/${fileName}`;
|
|
98
|
+
const eventId = `${base}/${eventPath}`;
|
|
99
|
+
const eventLogId = `${base}/${eventLog}`;
|
|
100
|
+
|
|
101
|
+
options['eventlog'] = {
|
|
102
|
+
'id': eventLogId ,
|
|
103
|
+
'file': `${options['public']}/${eventLog}` ,
|
|
104
|
+
'dir': `${options['public']}/${eventDir}` ,
|
|
105
|
+
'item': {
|
|
106
|
+
'id' : eventId ,
|
|
107
|
+
'file' : eventFile
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
36
111
|
await updateEventLog({path,options});
|
|
37
112
|
|
|
38
113
|
return { path, options, success: true };
|
|
@@ -51,11 +126,8 @@ async function updateEventLog({path,options}) {
|
|
|
51
126
|
const notification = fs.readFileSync(path, { encoding: 'utf-8'});
|
|
52
127
|
const notification_checksum = md5(notification);
|
|
53
128
|
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const entry = `${baseUrl}/${EVENT_DIR}/log/${fileName}`;
|
|
57
|
-
|
|
58
|
-
const eventLog = fsPath.join(options['public'],EVENT_DIR,EVENT_LOG);
|
|
129
|
+
const entry = options['eventlog']['item']['id'];
|
|
130
|
+
const eventLog = options['eventlog']['file'];
|
|
59
131
|
|
|
60
132
|
let json;
|
|
61
133
|
|
|
@@ -70,7 +142,7 @@ async function updateEventLog({path,options}) {
|
|
|
70
142
|
};
|
|
71
143
|
}
|
|
72
144
|
|
|
73
|
-
if (json['member'].findIndex( (e) => e === entry) >= 0) {
|
|
145
|
+
if (json['member'].findIndex( (e) => e['id'] === entry) >= 0) {
|
|
74
146
|
logger.info(`${entry} already in ${eventLog}`);
|
|
75
147
|
}
|
|
76
148
|
else {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const { dynamic_handler , parseAsJSON } = require('../lib/util');
|
|
2
2
|
const logger = require('../lib/util.js').getLogger();
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Demonstration notification handler that start multiple notification handlers.
|
|
6
|
+
* Requires a config file that specifies which handlers to start.
|
|
7
|
+
*/
|
|
4
8
|
async function handle({path,options}) {
|
|
5
9
|
let success = false;
|
|
6
10
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const md5 = require('md5');
|
|
3
|
+
const { parseAsJSON } = require('../lib/util.js');
|
|
4
|
+
const logger = require('../lib/util.js').getLogger();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Demonstration notification handler, that creates an 'Offer'
|
|
8
|
+
* notification for each incoming notificaation to request the
|
|
9
|
+
* archivation of the event log.
|
|
10
|
+
*/
|
|
11
|
+
async function handle({path,options}) {
|
|
12
|
+
logger.info(`parsing notification ${path}`);
|
|
13
|
+
|
|
14
|
+
const config = parseAsJSON(options['config']);
|
|
15
|
+
|
|
16
|
+
if (! config) {
|
|
17
|
+
logger.error('no configuration found for offer_memento_notification_handler');
|
|
18
|
+
return { path, options, success: false };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const thisConfig = config['notification_handler']?.['offer_memento'];
|
|
22
|
+
|
|
23
|
+
if (! thisConfig || !thisConfig['actor'] || !thisConfig['target']) {
|
|
24
|
+
logger.error('no actor/target entry for notification_handler.eventlog configuration');
|
|
25
|
+
return { path, options, success: false };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!options['artifact']) {
|
|
29
|
+
logger.info(`no artifact found (ignonring this request)`);
|
|
30
|
+
return { path, options, success: true };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!options['eventlog']) {
|
|
34
|
+
logger.info(`no artifact found (ignonring this request)`);
|
|
35
|
+
return { path, options, success: true };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const data = JSON.stringify({
|
|
40
|
+
'@context': [
|
|
41
|
+
"https://www.w3.org/ns/activitystreams" ,
|
|
42
|
+
{"schema": "https://schema.org/"}
|
|
43
|
+
],
|
|
44
|
+
type: 'Offer',
|
|
45
|
+
actor: thisConfig['actor'],
|
|
46
|
+
object: {
|
|
47
|
+
id: options['eventlog']['id'],
|
|
48
|
+
type: [ "Document", "schema:Dataset" ]
|
|
49
|
+
},
|
|
50
|
+
target: thisConfig['target']
|
|
51
|
+
},null,4);
|
|
52
|
+
|
|
53
|
+
const outboxFile = options['outbox'] + '/' + md5(data) + '.jsonld';
|
|
54
|
+
|
|
55
|
+
logger.info(`storing Offer to ${outboxFile}`);
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(outboxFile,data);
|
|
58
|
+
|
|
59
|
+
return { path, options, success: true };
|
|
60
|
+
}
|
|
61
|
+
catch(e) {
|
|
62
|
+
logger.error(`failed to process ${path}`);
|
|
63
|
+
logger.debug(e);
|
|
64
|
+
return { path, options, success: false };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { handle };
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
1
|
const logger = require('../lib/util.js').getLogger();
|
|
3
|
-
const { sendNotification } = require('../lib/util.js');
|
|
2
|
+
const { sendNotification , parseAsJSON } = require('../lib/util.js');
|
|
4
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Demonstration notification handler that sends a notification to a
|
|
6
|
+
* target inbox.
|
|
7
|
+
*/
|
|
5
8
|
async function handle({path,options}) {
|
|
6
9
|
try {
|
|
7
|
-
const
|
|
8
|
-
const data = JSON.parse(json);
|
|
10
|
+
const data = parseAsJSON(path);
|
|
9
11
|
const type = data['type'];
|
|
10
12
|
let inbox;
|
|
11
13
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { parseAsJSON } = require('../lib/util');
|
|
3
|
+
const logger = require('../lib/util.js').getLogger();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Demonstration notification handler, that checks if the notification
|
|
7
|
+
* matches a configurable list
|
|
8
|
+
*/
|
|
9
|
+
async function handle({path,options}) {
|
|
10
|
+
logger.info(`parsing notification ${path}`);
|
|
11
|
+
|
|
12
|
+
const config = parseAsJSON(options['config']);
|
|
13
|
+
|
|
14
|
+
if (! config) {
|
|
15
|
+
logger.error('no configuration found for eventlog_notification_handler');
|
|
16
|
+
return { path, options, success: false };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const thisConfig = config['notification_handler']?.['type_filter'];
|
|
20
|
+
|
|
21
|
+
if (! thisConfig) {
|
|
22
|
+
logger.error('no notification_handler.type_filer configuration');
|
|
23
|
+
return { path, options, success: false };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (! (thisConfig['anyOf'] && Array.isArray(thisConfig['anyOf']))) {
|
|
27
|
+
logger.error('no anyOf entry in notification_handler.type_filer should be an array');
|
|
28
|
+
return { path, options, success: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const json = parseAsJSON(path);
|
|
33
|
+
|
|
34
|
+
const type = json['type'];
|
|
35
|
+
|
|
36
|
+
const typeArray = Array.isArray(type) ? type : [type];
|
|
37
|
+
|
|
38
|
+
let isOk = true ;
|
|
39
|
+
|
|
40
|
+
for (let i = 0 ; i < typeArray.length ; i++) {
|
|
41
|
+
if (thisConfig['anyOf'].includes(typeArray[i])) {
|
|
42
|
+
// We are ok
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
logger.error(`${typeArray[i]} does not pass type_filter check`);
|
|
46
|
+
isOk = false;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isOk) {
|
|
52
|
+
return { path, options, success: true };
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return { path, options, success: false };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch(e) {
|
|
59
|
+
logger.error(`failed to process ${path}`);
|
|
60
|
+
logger.debug(e);
|
|
61
|
+
return { path, options, success: false };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { handle };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const { ldPropertyAsId , parseArtifact, parseAsJSON } = require('../lib/util.js');
|
|
2
|
+
const logger = require('../lib/util.js').getLogger();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Demonstration notification handler, that checks if the notification
|
|
6
|
+
* message contains an artifact that is known to the data node
|
|
7
|
+
*/
|
|
8
|
+
async function handle({path,options}) {
|
|
9
|
+
logger.info(`parsing notification ${path}`);
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const json = parseAsJSON(path);
|
|
13
|
+
|
|
14
|
+
let artifact = undefined;
|
|
15
|
+
|
|
16
|
+
if (ldPropertyAsId(json['context'])) {
|
|
17
|
+
artifact = ldPropertyAsId(json['context']);
|
|
18
|
+
}
|
|
19
|
+
else if (ldPropertyAsId(json['object'])) {
|
|
20
|
+
artifact = ldPropertyAsId(json['object']);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
logger.error(`failed to find valid context or object`);
|
|
24
|
+
return { path, options, success: false };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!artifact) {
|
|
28
|
+
logger.error(`failed to find artifact`);
|
|
29
|
+
return { path, options, success: false };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const artifactPath = parseArtifact(artifact,options);
|
|
33
|
+
|
|
34
|
+
if (artifactPath) {
|
|
35
|
+
// Storing the artifact path to the options.
|
|
36
|
+
// Maybe bad practice..but it is a workflow attribute like in Nifi :P
|
|
37
|
+
options['artifact'] = {
|
|
38
|
+
'id': artifact ,
|
|
39
|
+
'path': artifactPath
|
|
40
|
+
};
|
|
41
|
+
return { path, options, success: true };
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
logger.error(`artifact ${artifact} is not known here...`);
|
|
45
|
+
return { path, options, success: false };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch(e) {
|
|
49
|
+
logger.error(`failed to process ${path}`);
|
|
50
|
+
logger.debug(e);
|
|
51
|
+
return { path, options, success: false };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { handle };
|
package/lib/util.js
CHANGED
|
@@ -67,7 +67,7 @@ async function sendNotification(url,json,options) {
|
|
|
67
67
|
'Accept': 'application/json',
|
|
68
68
|
'Content-Type': 'application/json'
|
|
69
69
|
},
|
|
70
|
-
body: JSON.stringify(json)
|
|
70
|
+
body: JSON.stringify(json,null,4)
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
if (! response.ok) {
|
|
@@ -135,6 +135,60 @@ function parseAsJSON(path) {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
function ldPropertyAsId(object_or_string) {
|
|
139
|
+
if (!object_or_string) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (typeof object_or_string === 'string' || object_or_string instanceof String) {
|
|
144
|
+
return object_or_string;
|
|
145
|
+
}
|
|
146
|
+
else if (typeof object_or_string === 'object' && object_or_string['id']) {
|
|
147
|
+
return object_or_string['id'];
|
|
148
|
+
}
|
|
149
|
+
else if (typeof object_or_string === 'object' && object_or_string['@id']) {
|
|
150
|
+
return object_or_string['@id'];
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function parseArtifact(url,options) {
|
|
158
|
+
logger.debug(`isArtifact(${url})?`);
|
|
159
|
+
const base = options['base'] ? options['base'] :
|
|
160
|
+
options['host'] && options['port'] ?
|
|
161
|
+
`http://${options['host']}:${options['port']}` :
|
|
162
|
+
'http://localhost:8000';
|
|
163
|
+
|
|
164
|
+
const public = options['public'];
|
|
165
|
+
|
|
166
|
+
const filePath = url.substring(base.length + 1);
|
|
167
|
+
|
|
168
|
+
const metaPath = `${public}/${filePath}.meta`;
|
|
169
|
+
|
|
170
|
+
logger.debug(`searching ${metaPath}`);
|
|
171
|
+
|
|
172
|
+
if (! fs.existsSync(metaPath)) {
|
|
173
|
+
logger.debug(`no such file ${metaPath}`);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const json = parseAsJSON(metaPath);
|
|
178
|
+
|
|
179
|
+
if (json === null) {
|
|
180
|
+
logger.debug(`no json at ${metaPath}`);
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (json['X-Artifact']) {
|
|
185
|
+
return `${public}/${filePath}`;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
138
192
|
module.exports = {
|
|
139
193
|
getLogger ,
|
|
140
194
|
backOff_fetch ,
|
|
@@ -142,5 +196,7 @@ module.exports = {
|
|
|
142
196
|
moveTo ,
|
|
143
197
|
sendNotification ,
|
|
144
198
|
dynamic_handler ,
|
|
145
|
-
parseAsJSON
|
|
199
|
+
parseAsJSON ,
|
|
200
|
+
parseArtifact ,
|
|
201
|
+
ldPropertyAsId
|
|
146
202
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ldn-inbox-server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "A demonstration Event Notifications Inbox server",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"author": "Patrick Hochstenbach <Patrick.Hochstenbach@UGent.be>",
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"server": "npx ldn-inbox-server start-server",
|
|
10
10
|
"demo-post": "curl -X POST -H 'Content-Type: application/ld+json' --data-binary '@examples/offer.jsonld' http://localhost:8000/inbox/",
|
|
11
|
-
"handle-inbox": "npx ldn-inbox-server handler @inbox -hn ./handler/
|
|
11
|
+
"handle-inbox": "npx ldn-inbox-server handler @inbox -hn ./handler/accept_notification_handler.js",
|
|
12
12
|
"handle-outbox": "npx ldn-inbox-server handler @outbox -hn ./handler/send_notification_handler.js",
|
|
13
13
|
"handle-eventlog": "npx ldn-inbox-server handler @inbox -hn handler/eventlog_notification_handler.js",
|
|
14
14
|
"handle-inbox-multi": "npx ldn-inbox-server handler @inbox -hn ./handler/multi_notification_handler.js",
|
|
15
15
|
"handle-outbox-multi": "npx ldn-inbox-server handler @outbox -hn ./handler/multi_notification_handler.js",
|
|
16
|
-
"clean": "rm error/* inbox/* outbox/* public/events/* public/events/log/*"
|
|
16
|
+
"clean": "rm -rf error/* inbox/* outbox/* public/events/* public/events/log/* public/artifacts/artifact1 public/artifacts/artifact2 public/artifacts/*.jsonld*"
|
|
17
17
|
},
|
|
18
18
|
"bin": "./bin/ldn-inbox-server.js",
|
|
19
19
|
"keywords": [
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"node-fetch": "1.7.3",
|
|
32
32
|
"piscina": "^4.4.0",
|
|
33
33
|
"proper-lockfile": "^4.1.2",
|
|
34
|
+
"upath": "^2.0.1",
|
|
34
35
|
"uuid": "^9.0.1"
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<link
|
|
4
|
+
rel="stylesheet"
|
|
5
|
+
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
|
6
|
+
/>
|
|
7
|
+
<title>Artifact 1</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main class="container">
|
|
11
|
+
<h1>Artifact 1</h1>
|
|
12
|
+
<p>
|
|
13
|
+
<a href="artifact1.jsonld">Event Log</a>
|
|
14
|
+
</p>
|
|
15
|
+
</main>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<link
|
|
4
|
+
rel="stylesheet"
|
|
5
|
+
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
|
6
|
+
/>
|
|
7
|
+
<title>Artifact 2</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main class="container">
|
|
11
|
+
<h1>Artifact 2</h1>
|
|
12
|
+
<p>
|
|
13
|
+
<a href="artifact1.jsonld">Event Log</a>
|
|
14
|
+
</p>
|
|
15
|
+
</main>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|