Skip to content

Commit 5adf787

Browse files
davidlehndlongley
authored andcommitted
Improve @protected and protectedMode.
- Improve `@protected` support. - Allow options to be passed through various functions. - Add `protectedMode` support. - Temporarily just logging to console in warn mode.
1 parent 7dd4e63 commit 5adf787

File tree

4 files changed

+116
-46
lines changed

4 files changed

+116
-46
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Added
1010
- Testing: `skip` and `only` flags in manifests.
1111
- Testing: `VERBOSE_SKIP=true` env var to debug skipping.
12+
- Support `@protected` and `protectedMode`.
1213

1314
## 1.5.4 - 2019-02-28
1415

lib/compact.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ api.compact = ({
120120
// do value compaction on @values and subject references
121121
if(_isValue(element) || _isSubjectReference(element)) {
122122
const rval =
123-
api.compactValue({activeCtx, activeProperty, value: element});
123+
api.compactValue({activeCtx, activeProperty, value: element, options});
124124
if(options.link && _isSubjectReference(element)) {
125125
// store linked element
126126
if(!(element['@id'] in options.link)) {
@@ -295,7 +295,7 @@ api.compact = ({
295295
activeCtx.mappings[itemActiveProperty]['@nest'] : null;
296296
let nestResult = rval;
297297
if(nestProperty) {
298-
_checkNestProperty(activeCtx, nestProperty);
298+
_checkNestProperty(activeCtx, nestProperty, options);
299299
if(!_isObject(rval[nestProperty])) {
300300
rval[nestProperty] = {};
301301
}
@@ -324,7 +324,7 @@ api.compact = ({
324324
activeCtx.mappings[itemActiveProperty]['@nest'] : null;
325325
let nestResult = rval;
326326
if(nestProperty) {
327-
_checkNestProperty(activeCtx, nestProperty);
327+
_checkNestProperty(activeCtx, nestProperty, options);
328328
if(!_isObject(rval[nestProperty])) {
329329
rval[nestProperty] = {};
330330
}
@@ -795,10 +795,11 @@ api.compactIri = ({
795795
* @param activeCtx the active context.
796796
* @param activeProperty the active property that points to the value.
797797
* @param value the value to compact.
798+
* @param {Object} [options] - processing options.
798799
*
799800
* @return the compaction result.
800801
*/
801-
api.compactValue = ({activeCtx, activeProperty, value}) => {
802+
api.compactValue = ({activeCtx, activeProperty, value, options}) => {
802803
// value is a @value
803804
if(_isValue(value)) {
804805
// get context rules
@@ -873,7 +874,8 @@ api.compactValue = ({activeCtx, activeProperty, value}) => {
873874
}
874875

875876
// value is a subject reference
876-
const expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true});
877+
const expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true},
878+
options);
877879
const type = _getContextValue(activeCtx, activeProperty, '@type');
878880
const compacted = api.compactIri(
879881
{activeCtx, iri: value['@id'], relativeTo: {vocab: type === '@vocab'}});
@@ -1056,9 +1058,10 @@ function _selectTerm(
10561058
*
10571059
* @param activeCtx the active context.
10581060
* @param nestProperty a term in the active context or `@nest`.
1061+
* @param {Object} [options] - processing options.
10591062
*/
1060-
function _checkNestProperty(activeCtx, nestProperty) {
1061-
if(_expandIri(activeCtx, nestProperty, {vocab: true}) !== '@nest') {
1063+
function _checkNestProperty(activeCtx, nestProperty, options) {
1064+
if(_expandIri(activeCtx, nestProperty, {vocab: true}, options) !== '@nest') {
10621065
throw new JsonLdError(
10631066
'JSON-LD compact error; nested property must have an @nest value ' +
10641067
'resolving to @nest.',

lib/context.js

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,33 @@ api.process = ({activeCtx, localCtx, options,
7070
// We can't nullify if there are protected terms and we're
7171
// not in a term definition
7272
if(!propertyTermDefinition &&
73-
Object.keys(activeCtx.protected).length !== 0) {
73+
Object.keys(activeCtx.protected).length !== 0) {
74+
const protectedMode = (options && options.protectedMode) || 'error';
75+
if(protectedMode === 'error') {
76+
throw new JsonLdError(
77+
'Tried to nullify a context with protected terms outside of ' +
78+
'a term definition.',
79+
'jsonld.SyntaxError',
80+
{code: 'invalid context nullification'});
81+
} else if(protectedMode === 'warn') {
82+
// FIXME: remove logging and use a handler
83+
console.warn('WARNING: invalid context nullification');
84+
const oldActiveCtx = activeCtx;
85+
rval = activeCtx = api.getInitialContext(options);
86+
for(const [term, _protected] of
87+
Object.entries(oldActiveCtx.protected)) {
88+
if(_protected) {
89+
activeCtx.mappings[term] =
90+
util.clone(oldActiveCtx.mappings[term]);
91+
}
92+
}
93+
activeCtx.protected = util.clone(oldActiveCtx.protected);
94+
continue;
95+
}
7496
throw new JsonLdError(
75-
'Tried to nullify a context with protected terms outside of ' +
76-
'a term definition.',
97+
'Invalid protectedMode.',
7798
'jsonld.SyntaxError',
78-
{code: 'invalid context nullification'});
99+
{code: 'invalid protected mode', context: localCtx, protectedMode});
79100
}
80101
rval = activeCtx = api.getInitialContext(options);
81102
continue;
@@ -200,7 +221,7 @@ api.process = ({activeCtx, localCtx, options,
200221

201222
// process all other keys
202223
for(const key in ctx) {
203-
api.createTermDefinition(rval, ctx, key, defined);
224+
api.createTermDefinition(rval, ctx, key, defined, options);
204225
}
205226

206227
// cache result
@@ -220,8 +241,12 @@ api.process = ({activeCtx, localCtx, options,
220241
* @param term the term in the local context to define the mapping for.
221242
* @param defined a map of defining/defined keys to detect cycles and prevent
222243
* double definitions.
244+
* @param {Object} [options] - creation options.
245+
* @param {string} [options.protectedMode="error"] - "error" to throw error
246+
* on `@protected` constraint violation, "warn" to allow violations and
247+
* signal a warning.
223248
*/
224-
api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
249+
api.createTermDefinition = (activeCtx, localCtx, term, defined, options) => {
225250
if(term in defined) {
226251
// term already defined
227252
if(defined[term]) {
@@ -251,11 +276,24 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
251276
{code: 'invalid term definition', context: localCtx});
252277
}
253278

254-
if(activeCtx.protected[term]) {
279+
// FIXME if(1.1) ... ?
280+
if(term in activeCtx.protected) {
281+
const protectedMode = (options && options.protectedMode) || 'error';
282+
if(protectedMode === 'error') {
283+
throw new JsonLdError(
284+
'Invalid JSON-LD syntax; tried to redefine a protected term.',
285+
'jsonld.SyntaxError',
286+
{code: 'protected term redefinition', context: localCtx, term});
287+
} else if(protectedMode === 'warn') {
288+
// FIXME: remove logging and use a handler
289+
console.warn('WARNING: protected term redefinition', {term});
290+
return;
291+
}
255292
throw new JsonLdError(
256-
'Invalid JSON-LD syntax; tried to redefine a protected term.',
293+
'Invalid protectedMode.',
257294
'jsonld.SyntaxError',
258-
{code: 'protected term redefinition', context: localCtx, term});
295+
{code: 'invalid protected mode', context: localCtx, term,
296+
protectedMode});
259297
}
260298

261299
// remove old mapping
@@ -335,8 +373,9 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
335373
}
336374

337375
// expand and add @id mapping
338-
const id = api.expandIri(
339-
activeCtx, reverse, {vocab: true, base: false}, localCtx, defined);
376+
const id = _expandIri(
377+
activeCtx, reverse, {vocab: true, base: false}, localCtx, defined,
378+
options);
340379
if(!_isAbsoluteIri(id)) {
341380
throw new JsonLdError(
342381
'Invalid JSON-LD syntax; a @context @reverse value must be an ' +
@@ -355,8 +394,8 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
355394
}
356395
if(id !== term) {
357396
// expand and add @id mapping
358-
id = api.expandIri(
359-
activeCtx, id, {vocab: true, base: false}, localCtx, defined);
397+
id = _expandIri(
398+
activeCtx, id, {vocab: true, base: false}, localCtx, defined, options);
360399
if(!_isAbsoluteIri(id) && !api.isKeyword(id)) {
361400
throw new JsonLdError(
362401
'Invalid JSON-LD syntax; a @context @id value must be an ' +
@@ -378,7 +417,7 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
378417
const prefix = term.substr(0, colon);
379418
if(prefix in localCtx) {
380419
// define parent prefix
381-
api.createTermDefinition(activeCtx, localCtx, prefix, defined);
420+
api.createTermDefinition(activeCtx, localCtx, prefix, defined, options);
382421
}
383422

384423
if(activeCtx.mappings[prefix]) {
@@ -427,8 +466,9 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
427466

428467
if(type !== '@id' && type !== '@vocab') {
429468
// expand @type to full IRI
430-
type = api.expandIri(
431-
activeCtx, type, {vocab: true, base: false}, localCtx, defined);
469+
type = _expandIri(
470+
activeCtx, type, {vocab: true, base: false}, localCtx, defined,
471+
options);
432472
if(!_isAbsoluteIri(type)) {
433473
throw new JsonLdError(
434474
'Invalid JSON-LD syntax; an @context @type value must be an ' +
@@ -579,6 +619,25 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
579619
}
580620
};
581621

622+
/**
623+
* Expands a string to a full IRI. The string may be a term, a prefix, a
624+
* relative IRI, or an absolute IRI. The associated absolute IRI will be
625+
* returned.
626+
*
627+
* @param activeCtx the current active context.
628+
* @param value the string to expand.
629+
* @param relativeTo options for how to resolve relative IRIs:
630+
* base: true to resolve against the base IRI, false not to.
631+
* vocab: true to concatenate after @vocab, false not to.
632+
* @param {Object} [options] - processing options.
633+
*
634+
* @return the expanded value.
635+
*/
636+
api.expandIri = (activeCtx, value, relativeTo, options) => {
637+
return _expandIri(activeCtx, value, relativeTo, undefined, undefined,
638+
options);
639+
};
640+
582641
/**
583642
* Expands a string to a full IRI. The string may be a term, a prefix, a
584643
* relative IRI, or an absolute IRI. The associated absolute IRI will be
@@ -593,18 +652,19 @@ api.createTermDefinition = (activeCtx, localCtx, term, defined) => {
593652
* during context processing).
594653
* @param defined a map for tracking cycles in context definitions (only given
595654
* if called during context processing).
655+
* @param {Object} [options] - processing options.
596656
*
597657
* @return the expanded value.
598658
*/
599-
api.expandIri = (activeCtx, value, relativeTo, localCtx, defined) => {
659+
function _expandIri(activeCtx, value, relativeTo, localCtx, defined, options) {
600660
// already expanded
601661
if(value === null || !_isString(value) || api.isKeyword(value)) {
602662
return value;
603663
}
604664

605665
// define term dependency if not defined
606666
if(localCtx && value in localCtx && defined[value] !== true) {
607-
api.createTermDefinition(activeCtx, localCtx, value, defined);
667+
api.createTermDefinition(activeCtx, localCtx, value, defined, options);
608668
}
609669

610670
relativeTo = relativeTo || {};
@@ -636,7 +696,7 @@ api.expandIri = (activeCtx, value, relativeTo, localCtx, defined) => {
636696

637697
// prefix dependency not defined, define it
638698
if(localCtx && prefix in localCtx) {
639-
api.createTermDefinition(activeCtx, localCtx, prefix, defined);
699+
api.createTermDefinition(activeCtx, localCtx, prefix, defined, options);
640700
}
641701

642702
// use mapping if prefix is defined
@@ -660,7 +720,7 @@ api.expandIri = (activeCtx, value, relativeTo, localCtx, defined) => {
660720
}
661721

662722
return value;
663-
};
723+
}
664724

665725
/**
666726
* Gets the initial context.

0 commit comments

Comments
 (0)