Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@rmlio/yarrrml-parser

Package Overview
Dependencies
Maintainers
3
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rmlio/yarrrml-parser - npm Package Compare versions

Comparing version 1.4.0 to 1.5.0

test/ldes/custom-ldes-id/mapping.rml.ttl

1

bin/parser.js

@@ -17,2 +17,3 @@ #!/usr/bin/env node

const namespaces = require('prefix-ns').asMap();
namespaces['rml'] = 'http://semweb.mmlab.be/ns/rml#'; // this one is the only official one for now.
const watch = require('../lib/watcher.js');

@@ -19,0 +20,0 @@ const glob = require('glob');

@@ -10,2 +10,13 @@ # Changelog

## [1.5.0] - 2023-06-13
### Added
- Support for LDES
### Fixed
- expander: fix graphs example from spec (see [issue 188](https://github.com/RMLio/yarrrml-parser/issues/188))
- wrong namespace gets fetched from prefix.cc for rml prefix. Hardcode it.
## [1.4.0] - 2022-11-18

@@ -280,2 +291,3 @@

[1.5.0]: https://github.com/RMLio/yarrrml-parser/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/RMLio/yarrrml-parser/compare/v1.3.6...v1.4.0

@@ -282,0 +294,0 @@ [1.3.6]: https://github.com/RMLio/yarrrml-parser/compare/v1.3.5...v1.3.6

@@ -40,3 +40,3 @@ /**

expandTargetsInDocument(output);
rewriteLDESes(output);
return output;

@@ -56,5 +56,5 @@ }

expandSourcesInMapping(mapping, mappingKey);
expandTargetsInMapping(mapping, mappingKey);
expandPredicateObjects(mapping, mappingKey);
expandGraphs(mapping);
expandTargetsInMapping(mapping);
} else {

@@ -75,2 +75,7 @@ Logger.warn(`mapping "${mappingKey}": no rules are provided. Skipping.`);

if (typeof mapping.subjects === 'string') {
if (mapping.subjects.endsWith('~ldes')) {
mapping.subjects = mapping.subjects.slice(0, -5)
Logger.debug('LDES annotation found');
mapping.subjects = expandLDES(mapping.subjects);
}
mapping.subjects = [mapping.subjects]

@@ -80,2 +85,3 @@ } else if (Array.isArray(mapping.subjects)) {

if (typeof mapping.subjects[i] === 'object') {
mapping.subjects[i] = expandLDES(mapping.subjects[i])
expandFunction(mapping.subjects[i]);

@@ -141,5 +147,3 @@

function expandTargetsInMapping(mapping, mappingKey) {
let targets = [];
function expandTargetsInMapping(mapping) {
// Replace shortcuts

@@ -150,3 +154,3 @@ replaceAll('targets', mapping);

if (mapping.subjects) {
mapping.subjects.forEach((subject) => {
mapping.subjects.forEach(subject => {
if (subject.targets) {

@@ -410,3 +414,3 @@ if (Array.isArray(subject.targets)) {

expandFunction(po.objects[j]);
expandFunction(po.objects[j], true);

@@ -423,3 +427,3 @@ //condition

po.objects[j].conditions.forEach(c => {
expandFunction(c);
expandFunction(c, true);
});

@@ -449,3 +453,3 @@

function expandFunction(input) {
function expandFunction(input, canHaveObject = false) {
replaceAll('function', input);

@@ -486,7 +490,5 @@ replaceAll('parameters', input);

if (e[2] === "subject" || e[2] === "object") {
input.parameters[i].from = e[2];
input.parameters[i].from = canHaveObject ? e[2] : "subject";
} else {
const e = new Error(`\`from\` has to have the value "s", "subject", "o", or "object`);
e.code = 'INVALID_YAML';
throw e;
Logger.error(`\`from\` has to have the value "s", "subject", "o", or "object`);
}

@@ -497,3 +499,3 @@ } else {

} else if (e[0] === 'str2') {
input.parameters[i].from = "object"
input.parameters[i].from = canHaveObject ? "object" : "subject";
} else {

@@ -511,3 +513,3 @@ // I know this can be written shorter, but makes for a bit more clearer code

if (e.value instanceof Object) {
expandFunction(e.value);
expandFunction(e.value, canHaveObject);
e.from = 'function';

@@ -737,2 +739,262 @@ }

function expandLDES(subject) {
let ldes = {};
if (subject !== undefined) {
if (typeof subject === 'string') {
ldes.memberSubject = subject;
subject = {
value: subject,
};
} else if (typeof subject === 'object' && subject !== null) {
if (subject.ldes !== undefined && subject.ldes !== null) {
ldes = subject.ldes;
ldes.memberSubject = subject.value
} else {
// there is no LDES
return subject;
}
}
} else {
// no subject, no LDES
return subject;
}
// the 'shape' key is just copied...
if (ldes.id === undefined || ldes.id === null) {
ldes.id = 'http://example.org/eventstream';
}
// TODO: watchedProperties: allow shortcut watchedProperties: <property str> ?
if (ldes.watchedProperties === undefined) {
ldes.watchedProperties = [];
} else {
// just check if it's an array
if (!Array.isArray(ldes.watchedProperties)) {
Logger.error('LDES `watchedProperties` must be an array.');
}
}
// TODO: versionOfPath: allow shortcut versionOfPath: <predicate> ?
if (ldes.versionOfPath !== undefined && ldes.versionOfPath !== null) {
let newVersionOfPath = {};
if (Array.isArray(ldes.versionOfPath)) {
if (ldes.versionOfPath.length === 0) {
newVersionOfPath.predicate = 'http://purl.org/dc/terms/isVersionOf';
newVersionOfPath.object = null;
} else if (ldes.versionOfPath.length === 1) {
newVersionOfPath.predicate = ldes.versionOfPath[0];
newVersionOfPath.object = null;
} else if (ldes.versionOfPath.length === 2) {
newVersionOfPath.predicate = ldes.versionOfPath[0];
newVersionOfPath.object = ldes.versionOfPath[1];
if (!newVersionOfPath.object.endsWith('~iri')) {
newVersionOfPath.object += '~iri';
}
} else {
Logger.error('LDES `versionOfPath` must be an array of maximum two strings.');
}
} else {
Logger.error('LDES `versionOfPath` must be an array.');
}
const expandedpos = expandPredicateAndObject(newVersionOfPath.predicate, newVersionOfPath.object);
newVersionOfPath.predicate = expandedpos[0];
newVersionOfPath.object = expandedpos[1];
ldes.versionOfPath = newVersionOfPath;
} else {
ldes.versionOfPath = null;
}
// TODO: timestampPath: allow shortcut timestampPath: <predicate> ?
if (ldes.timestampPath !== undefined && ldes.timestampPath !== null) {
let newTimestampPath = {};
if (Array.isArray(ldes.timestampPath)) {
if (ldes.timestampPath.length >= 1) {
newTimestampPath.predicate = ldes.timestampPath[0];
newTimestampPath.object = null;
newTimestampPath.oDataType = null;
// TODO the predicate and object have to be present as predicateobject mapping; check later?
if (ldes.timestampPath.length >= 2) {
newTimestampPath.object = ldes.timestampPath[1];
// TODO: add predicateobjectmapping if not exists
if (ldes.timestampPath.length === 3) {
newTimestampPath.oDataType = ldes.timestampPath[2];
} else {
Logger.error('LDES `timestampPath` must be an array of maximum three strings.');
}
}
} else {
Logger.error('LDES `timestampPath` must be an array of minimum one string.');
}
} else {
Logger.error('LDES timestampPath must be an array.');
}
const expandedpos = expandPredicateAndObject(newTimestampPath.predicate, newTimestampPath.object);
newTimestampPath.predicate = expandedpos[0];
newTimestampPath.object = expandedpos[1];
ldes.timestampPath = newTimestampPath;
} else {
ldes.timestampPath = null;
}
if (ldes.memberIDFunction !== undefined && ldes.memberIDFunction !== null) {
expandFunction(ldes.memberIDFunction);
} else {
// use the default generateUniqueIRI function, so prepare parameters
const watchedPropertiesStr = _toWatchedPropertiesString(ldes.watchedProperties);
ldes.memberIDFunction = {
function: idlabfn + 'generateUniqueIRI',
parameters: [
{
parameter: idlabfn + 'iri',
value: ldes.memberSubject,
from: 'subject',
type: 'iri',
},
{
parameter: idlabfn + 'watchedProperty',
value: watchedPropertiesStr,
from: 'subject',
type: 'literal'
}
],
};
}
if (ldes.shape === undefined) {
ldes.shape = null;
}
subject.ldes = ldes;
return subject;
}
/**
* This rewrites the mappings so that LDES specific triples can be generated
* according to the CURRENT implementation in RMLMapper. If that implementation changes,
* and it will, then this function definitely needs to change.
* (see https://dylanvanassche.be/assets/pdf/eswc2022-rml-ldes.pdf)
* @param document the YARRRML document to rewrite LDES stuff for.
*/
function rewriteLDESes(document) {
if (document.mappings !== undefined && document.mappings !== null) {
for (let mapping of Object.values(document.mappings)) {
for (let subject of mapping.subjects) {
if (subject.ldes !== null && subject.ldes !== undefined) {
const ldes = subject.ldes;
if (subject.targets !== null && subject.targets !== undefined) {
for (let target of subject.targets) {
// if the target is of type string, then it's a reference to a target definition
let realTarget = typeof target === 'string' ? document.targets[target] : target;
// make the target an LDES target!
realTarget.ldes = {
id: ldes.id,
versionOfPath: ldes.versionOfPath,
timestampPath: ldes.timestampPath,
shape: ldes.shape
};
}
// check / add LDES-related predicate-object mappings for versionOfPath
if (ldes.versionOfPath !== null) {
// check if predicate & object mappings exist. If not, add them.
addLDESSpecificPredicateObjects(ldes.versionOfPath.predicate, ldes.versionOfPath.object, mapping.predicateobjects);
}
// check / add LDES-related predicate-object mappings for timestampPath
if (ldes.timestampPath !== null) {
// check if predicate & object mappings exist. If not, add them.
addLDESSpecificPredicateObjects(ldes.timestampPath.predicate, ldes.timestampPath.object, mapping.predicateobjects);
}
// remove ldes properties object from subject
delete subject.ldes;
// replace the subject with the LDES member id function
subject.function = ldes.memberIDFunction.function;
subject.parameters = ldes.memberIDFunction.parameters;
delete subject.value;
} else { // If there are no targets, we have to add an LDES target!
// TODO: for the current implementation of LDES in RML, there HAS to be a target.
// This will change in the upcoming new implementation
Logger.error('The current RML LDES generation mappings require a target at subject mapping level. ' +
'This will change in the future.');
}
}
}
}
}
}
function addLDESSpecificPredicateObjects(predicate, object, predicateobjects) {
let found = false;
for (let predicateobject of predicateobjects) {
for (let i = 0; i < predicateobject.predicates.length; i++) {
// if the predicate exists, the object must be the same if the given object is not null
if (predicateobject.predicates[i] === predicate) {
found = true;
if (object !== null && predicateobject.objects[i] !== object) {
Logger.error(`The versionOf or timestampPath predicate ${predicate} expects an object mapping ${object}, but is ${predicateobject.objects[i]}`);
}
}
}
}
if (!found) {
predicateobjects.push({
predicates: [predicate],
objects: [object]
});
}
}
// helper function to put LDES watched properties into one string to pass to generateUniqueIRI function
function _toWatchedPropertiesString(watchedPropertiesArray) {
let resultStr = '';
if (watchedPropertiesArray.length > 0) {
const watchedProperty = _parseTemplate(watchedPropertiesArray[0]);
resultStr = watchedProperty.concat('=$(').concat(watchedProperty).concat(')');
}
for (let i = 1; i < watchedPropertiesArray.length; i++) {
const watchedProperty = _parseTemplate(watchedPropertiesArray[i]);
const wpStr = '&'.concat(watchedProperty).concat('=$(').concat(watchedProperty).concat(')');
resultStr = resultStr.concat(wpStr);
}
return resultStr;
}
// TODO: Almost literally copied from abstract-generator.js. Is there an elegant way of re-using that function?
function _parseTemplate(t) {
t = '' + t; // Make sure it's a string.
t = t.replace(/\\\\/g, '@@@BACKWARD-SLASH@@@'); // We want to preserve real backward slashes.
t = t.replace(/\\\(/g, '@@@BRACKET-OPEN@@@'); // Same for opening brackets.
t = t.replace(/\\\)/g, '@@@BRACKET-CLOSE@@@'); // Same for closing brackets.
t = t.replace(/\$\(([^)]*)\)/g, "$1");
t = t.replace(/@@@BRACKET-CLOSE@@@/g, ')');
t = t.replace(/@@@BRACKET-OPEN@@@/g, '(');
t = t.replace(/@@@BACKWARD-SLASH@@@/g, '/');
return t;
}
function expandPredicateAndObject(predicate, object) {
const predicateobjects = [{
predicates: [predicate],
objects: [object]
}];
expandPredicates(predicateobjects);
if (object !== null) {
expandObjects(predicateobjects);
return [predicateobjects[0].predicates[0], predicateobjects[0].objects[0]];
} else {
return [predicateobjects[0].predicates[0], null];
}
}
module.exports = expand;

@@ -23,2 +23,4 @@ /**

namespaces.comp = 'http://semweb.mmlab.be/ns/rml-compression#';
namespaces.ldes = 'https://w3id.org/ldes#';
namespaces.tree = 'https://w3id.org/tree#';

@@ -182,3 +184,7 @@ class RMLGenerator extends AbstractGenerator {

Object.keys(target).forEach(key => {
id += target[key]
if (key === 'ldes') {
id += 'ldes';
} else {
id += target[key];
}
id += '-'

@@ -475,9 +481,9 @@ });

if (targetName) {
this.quads.push(quad(
tSubject,
namedNode(namespaces.rdfs + 'label'),
literal(targetName)
));
}
if (targetName) {
this.quads.push(quad(
tSubject,
namedNode(namespaces.rdfs + 'label'),
literal(targetName)
));
}

@@ -707,2 +713,50 @@ /* Serialization format */

// Add LDES specific triples, if any
if (target.ldes) {
this.prefixes['ldes'] = namespaces.ldes;
this.prefixes['tree'] = namespaces.tree;
// say it's an LDES logical target
this.quads.push(quad(
tSubject,
namedNode(namespaces.rdf + 'type'),
namedNode(namespaces.ldes + 'EventStreamTarget'),
));
// add the required LDES "base" iri
this.quads.push(quad(
tSubject,
namedNode(namespaces.ldes + 'baseIRI'),
namedNode(target.ldes.id),
));
// Add the SHACL shape. While, according to the LDES spec it is optional,
// the current RMLMapper implementation requires it.
const shaclshape = target.ldes.shape ? target.ldes.shape : 'http://example.org/shape.shacl';
this.quads.push(quad(
tSubject,
namedNode(namespaces.tree + 'shape'),
namedNode(shaclshape),
));
// add the timestampPath
if (target.ldes.timestampPath) {
this.quads.push(quad(
tSubject,
namedNode(namespaces.ldes + 'timestampPath'),
namedNode(target.ldes.timestampPath.predicate),
));
}
// add the versionOfPath
if (target.ldes.versionOfPath) {
this.quads.push(quad(
tSubject,
namedNode(namespaces.ldes + 'versionOfPath'),
namedNode(target.ldes.versionOfPath.predicate),
));
}
}
return tSubject;

@@ -709,0 +763,0 @@ }

@@ -19,2 +19,5 @@ /**

doTestCase('spec/mapping-with-database-as-source', options);
doTestCase('spec/graphs-all-triples', options);
});

@@ -396,2 +399,12 @@

});
describe('LDES', () => {
it('works for watched properties', function (done) {
work('ldes/watched-properties-only/mapping.yaml', 'ldes/watched-properties-only/mapping.rml.ttl', done, {includeMetadata: false});
});
it('works for a custom LDES ID', function (done) {
work('ldes/custom-ldes-id/mapping.yaml', 'ldes/custom-ldes-id/mapping.rml.ttl', done, {includeMetadata: false});
});
});
});

@@ -398,0 +411,0 @@

@@ -55,3 +55,3 @@ const assert = require('assert');

} catch (e) {
assert.deepStrictEqual(yamlQuads.map(q => q.toString()).sort(), rmlQuads.map(q => q.toString()).sort());
assert.equal(yamlQuads.map(q => quadToBadString(q)).sort().join('\n'), rmlQuads.map(q => quadToBadString(q)).sort().join('\n'));
}

@@ -64,2 +64,6 @@ cb();

function quadToBadString(quad) {
return `<${quad.subject.value}> <${quad.predicate.value}> <${quad.object.value}>`
}
function compareY2R2RData(yaml, ttl, options, cb) {

@@ -66,0 +70,0 @@ const y2r = new convertYAMLtoR2RML(options);

2

package.json
{
"name": "@rmlio/yarrrml-parser",
"version": "1.4.0",
"version": "1.5.0",
"description": "Parse YARRRML descriptions into RML RDF statements",

@@ -5,0 +5,0 @@ "main": "lib/yarrrml2rml.js",

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc