Skip to content

Commit e050ad8

Browse files
committed
Update events and validation modes.
- Rename handlers. - Add strict handler docs. - Add `JsonLdEvent` type to events. - Update `safeEventHandler` to handle new event codes. - Add more event emitting points. - Update event testing and tests. - Add safe/strict testing flags. - Fix bugs and cleanup.
1 parent ebd4a1f commit e050ad8

File tree

7 files changed

+321
-76
lines changed

7 files changed

+321
-76
lines changed

README.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,11 @@ quality is known events are unnecessary.
363363

364364
#### Event Structure
365365

366-
Events are basic JSON objects with the following properties:
366+
Events are JSON objects with the following properties:
367367

368+
- **`type`**: ['JsonLdEvent'] and optionally an array with others.
368369
- **`code`**: A basic string code, similar to existing JSON-LD error codes.
369370
- **`level`**: The severity level. Currently only `warning` is emitted.
370-
- **`tags`**: Optional hints for the type of event. Currently defined:
371-
- **`unsafe`**: Event is considered unsafe.
372-
- **`lossy`**: Event is related to potential data loss.
373-
- **`empty`**: Event is related to empty data structures.
374371
- **`message`**: A human readable message describing the event.
375372
- **`details`**: A JSON object with event specific details.
376373

@@ -445,7 +442,7 @@ const handler = {
445442
const expanded = await jsonld.expand(data, {eventHandler});
446443
```
447444

448-
#### Safe Mode
445+
#### Safe Validation
449446

450447
A common use case is to avoid JSON-LD constructs that will result in lossy
451448
behavior. The JSON-LD specifications have notes about when data is dropped.
@@ -473,15 +470,29 @@ const expanded = await jsonld.expand(data, {
473470
});
474471
```
475472

473+
#### Strict Validation
474+
475+
Some data may be valid and "safe" but still have issues that could indicate
476+
data problems. A "strict" validation mode is available that handles more issues
477+
that the "safe" validation mode. This mode may cause false positives so may be
478+
best suited for JSON-LD authoring tools.
479+
480+
```js
481+
// expand a document in strict mode
482+
const expanded = await jsonld.expand(data, {
483+
eventHandler: jsonld.strictEventHandler
484+
});
485+
```
486+
476487
#### Available Handlers
477488

478489
Some predefined event handlers are available to use alone or as part of a more
479490
complex handler:
480491

481-
- **safeModeEventHandler**: The handler used when `safe` is `true`.
482-
- **strictModeEventHandler**: A handler that is more strict than the `safe`
483-
handler and also fails on other detectable events related to poor input
484-
structure.
492+
- **safeEventHandler**: The handler used when `safe` is `true`.
493+
- **strictEventHandler**: A handler that is more strict than the `safe`
494+
handler and also fails on other detectable events related to possible input
495+
issues.
485496
- **logEventHandler**: A debugging handler that outputs to the console.
486497
- **logWarningHandler**: A debugging handler that outputs `warning` level
487498
events to the console.
@@ -495,13 +506,13 @@ safe mode, and the second handler when in safe mode.
495506
```js
496507
// fail on unknown events
497508
jsonld.setDefaultEventHandler(jsonld.unhandledEventHandler);
498-
// will use safe mode handler, like `{safe: true}`
509+
// will use unhandled event handler by default
499510
const expanded = await jsonld.expand(data);
500511
```
501512

502513
```js
503514
// always use safe mode event handler, ignore other events
504-
jsonld.setDefaultEventHandler(jsonld.safeModeEventHandler);
515+
jsonld.setDefaultEventHandler(jsonld.safeEventHandler);
505516
// will use safe mode handler, like `{safe: true}`
506517
const expanded = await jsonld.expand(data);
507518
```

lib/context.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ api.createTermDefinition = ({
479479
if(options.eventHandler) {
480480
_handleEvent({
481481
event: {
482+
type: ['JsonLdEvent'],
482483
code: 'invalid reserved term',
483484
level: 'warning',
484485
message:
@@ -573,6 +574,7 @@ api.createTermDefinition = ({
573574
if(options.eventHandler) {
574575
_handleEvent({
575576
event: {
577+
type: ['JsonLdEvent'],
576578
code: 'invalid reserved value',
577579
level: 'warning',
578580
message:
@@ -621,6 +623,7 @@ api.createTermDefinition = ({
621623
if(options.eventHandler) {
622624
_handleEvent({
623625
event: {
626+
type: ['JsonLdEvent'],
624627
code: 'invalid reserved value',
625628
level: 'warning',
626629
message:
@@ -1155,6 +1158,7 @@ function _expandIri(activeCtx, value, relativeTo, localCtx, defined, options) {
11551158
if(options.eventHandler) {
11561159
_handleEvent({
11571160
event: {
1161+
type: ['JsonLdEvent'],
11581162
code: 'relative IRI after expansion',
11591163
level: 'warning',
11601164
message: 'Expansion resulted in a relative IRI.',

lib/events.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ api.defaultEventHandler = null;
3434
api.setupEventHandler = ({options = {}}) => {
3535
// build in priority order
3636
const eventHandler = [].concat(
37-
options.safe ? api.safeModeEventHandler : [],
37+
options.safe ? api.safeEventHandler : [],
3838
options.eventHandler ? _asArray(options.eventHandler) : [],
3939
api.defaultEventHandler ? api.defaultEventHandler : []
4040
);
@@ -100,27 +100,43 @@ function _handle({event, handlers}) {
100100
return doNext;
101101
}
102102

103+
const _notSafeEventCodes = new Set([
104+
'dropping object with only @id',
105+
'dropping object with only @list',
106+
'dropping object with only @value',
107+
'dropping empty object',
108+
'dropping free-floating scalar',
109+
'invalid @language value',
110+
'invalid property expansion',
111+
'no value after expansion',
112+
'relative IRI after expansion'
113+
]);
114+
103115
// safe handler that rejects unsafe warning conditions
104-
api.safeModeEventHandler = function safeModeEventHandler({event, next}) {
116+
api.safeEventHandler = function safeEventHandler({event, next}) {
105117
// fail on all unsafe warnings
106-
if(event.level === 'warning' &&
107-
event.tags && event.tags.includes('unsafe')) {
118+
if(event.level === 'warning' && _notSafeEventCodes.has(event.code)) {
108119
throw new JsonLdError(
109-
'Safe mode violation.',
110-
'jsonld.SafeModeViolationEvent',
120+
'Safe mode validation error.',
121+
'jsonld.ValidationError',
111122
{event}
112123
);
113124
}
114125
next();
115126
};
116127

128+
const _notStrictEventCodes = new Set([
129+
..._notSafeEventCodes,
130+
'invalid reserved term'
131+
]);
132+
117133
// strict handler that rejects all warning conditions
118-
api.strictModeEventHandler = function strictModeEventHandler({event, next}) {
134+
api.strictEventHandler = function strictEventHandler({event, next}) {
119135
// fail on all warnings
120-
if(event.level === 'warning') {
136+
if(event.level === 'warning' && _notStrictEventCodes.has(event.code)) {
121137
throw new JsonLdError(
122-
'Strict mode violation.',
123-
'jsonld.StrictModeViolationEvent',
138+
'Strict mode validation error.',
139+
'jsonld.ValidationError',
124140
{event}
125141
);
126142
}

lib/expand.js

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ api.expand = async ({
106106
insideList
107107
});
108108
if(mapped === undefined) {
109+
// FIXME
110+
if(options.eventHandler) {
111+
_handleEvent({
112+
event: {
113+
type: ['JsonLdEvent'],
114+
code: 'dropping free-floating scalar',
115+
level: 'warning',
116+
message: 'Dropping a free-floating scalar not in a list.',
117+
details: {
118+
value: element
119+
}
120+
},
121+
options
122+
});
123+
}
109124
return null;
110125
}
111126
return mapped;
@@ -148,6 +163,21 @@ api.expand = async ({
148163
insideList
149164
});
150165
if(e === undefined) {
166+
// FIXME name, desc
167+
if(options.eventHandler) {
168+
_handleEvent({
169+
event: {
170+
type: ['JsonLdEvent'],
171+
code: 'no value after expansion',
172+
level: 'warning',
173+
message: 'Expansion did not result in any value.',
174+
details: {
175+
value: element[i]
176+
}
177+
},
178+
options
179+
});
180+
}
151181
continue;
152182
}
153183
}
@@ -317,6 +347,21 @@ api.expand = async ({
317347
if(mapped !== undefined) {
318348
rval = mapped;
319349
} else {
350+
// FIXME
351+
if(options.eventHandler) {
352+
_handleEvent({
353+
event: {
354+
type: ['JsonLdEvent'],
355+
code: 'no value after expansion',
356+
level: 'warning',
357+
message: 'Dropping null value from expansion.',
358+
details: {
359+
value: rval
360+
}
361+
},
362+
options
363+
});
364+
}
320365
rval = null;
321366
}
322367
} else if(!values.every(v => (_isString(v) || _isEmptyObject(v))) &&
@@ -365,6 +410,21 @@ api.expand = async ({
365410
if(mapped !== undefined) {
366411
rval = mapped;
367412
} else {
413+
// FIXME
414+
if(options.eventHandler) {
415+
_handleEvent({
416+
event: {
417+
type: ['JsonLdEvent'],
418+
code: 'dropping object with only @language',
419+
level: 'warning',
420+
message: 'Dropping object with only @language.',
421+
details: {
422+
value: rval
423+
}
424+
},
425+
options
426+
});
427+
}
368428
rval = null;
369429
}
370430
}
@@ -388,6 +448,37 @@ api.expand = async ({
388448
if(mapped !== undefined) {
389449
rval = mapped;
390450
} else {
451+
// FIXME
452+
if(options.eventHandler) {
453+
// FIXME: one event or diff event for empty, @v/@l, {@id}?
454+
let code;
455+
let message;
456+
if(count === 0) {
457+
code = 'dropping empty object';
458+
message = 'Dropping empty object.';
459+
} else if('@value' in rval) {
460+
code = 'dropping object with only @value';
461+
message = 'Dropping object with only @value.';
462+
} else if('@list' in rval) {
463+
code = 'dropping object with only @list';
464+
message = 'Dropping object with only @list.';
465+
} else if(count === 1 && '@id' in rval) {
466+
code = 'dropping object with only @id';
467+
message = 'Dropping object with only @id.';
468+
}
469+
_handleEvent({
470+
event: {
471+
type: ['JsonLdEvent'],
472+
code,
473+
level: 'warning',
474+
message,
475+
details: {
476+
value: rval
477+
}
478+
},
479+
options
480+
});
481+
}
391482
rval = null;
392483
}
393484
}
@@ -454,6 +545,7 @@ async function _expandObject({
454545
if(expandedProperty === null ||
455546
!(_isAbsoluteIri(expandedProperty) || _isKeyword(expandedProperty))) {
456547
// TODO: use `await` to support async
548+
const _expandedProperty = expandedProperty;
457549
expandedProperty = expansionMap({
458550
unmappedProperty: key,
459551
activeCtx,
@@ -468,12 +560,13 @@ async function _expandObject({
468560
if(options.eventHandler) {
469561
_handleEvent({
470562
event: {
563+
type: ['JsonLdEvent'],
471564
code: 'invalid property expansion',
472565
level: 'warning',
473566
message: 'Invalid expansion for property.',
474567
details: {
475-
// FIXME: include expandedProperty before mapping
476-
property: key
568+
property: key,
569+
expandedProperty: _expandedProperty
477570
}
478571
},
479572
options
@@ -633,6 +726,7 @@ async function _expandObject({
633726
if(options.eventHandler) {
634727
_handleEvent({
635728
event: {
729+
type: ['JsonLdEvent'],
636730
code: 'invalid @language value',
637731
level: 'warning',
638732
message: '@language value must be valid BCP47.',

lib/fromRdf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ function _RDFToObject(o, useNativeTypes, rdfDirection, options) {
344344
if(options.eventHandler) {
345345
_handleEvent({
346346
event: {
347+
type: ['JsonLdEvent'],
347348
code: 'invalid @language value',
348349
level: 'warning',
349350
message: '@language value must be valid BCP47.',

lib/jsonld.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ const {
8383
const {
8484
logEventHandler: _logEventHandler,
8585
logWarningEventHandler: _logWarningEventHandler,
86+
safeEventHandler: _safeEventHandler,
8687
setDefaultEventHandler: _setDefaultEventHandler,
8788
setupEventHandler: _setupEventHandler,
88-
strictModeEventHandler: _strictModeEventHandler,
89+
strictEventHandler: _strictEventHandler,
8990
unhandledEventHandler: _unhandledEventHandler
9091
} = require('./events');
9192

@@ -1018,8 +1019,9 @@ jsonld.url = require('./url');
10181019
/* Events API and handlers */
10191020
jsonld.logEventHandler = _logEventHandler;
10201021
jsonld.logWarningEventHandler = _logWarningEventHandler;
1022+
jsonld.safeEventHandler = _safeEventHandler;
10211023
jsonld.setDefaultEventHandler = _setDefaultEventHandler;
1022-
jsonld.strictModeEventHandler = _strictModeEventHandler;
1024+
jsonld.strictEventHandler = _strictEventHandler;
10231025
jsonld.unhandledEventHandler = _unhandledEventHandler;
10241026

10251027
/* Utility API */

0 commit comments

Comments
 (0)