feat: Support metadata in multiple graphs

This commit is contained in:
Joachim Van Herwegen
2021-06-28 15:19:58 +02:00
parent e7ff134b25
commit 35a7cf988c
4 changed files with 120 additions and 45 deletions

View File

@@ -1,18 +1,35 @@
import 'jest-rdf';
import { literal, namedNode, quad } from '@rdfjs/data-model';
import type { Literal, NamedNode, Quad } from 'rdf-js';
import { defaultGraph, literal, namedNode, quad } from '@rdfjs/data-model';
import type { NamedNode, Quad } from 'rdf-js';
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
import { CONTENT_TYPE } from '../../../../src/util/Vocabularies';
// Helper functions to filter quads
function getQuads(quads: Quad[], subject?: string, predicate?: string, object?: string, graph?: string): Quad[] {
return quads.filter((qq): boolean =>
(!subject || qq.subject.value === subject) &&
(!predicate || qq.predicate.value === predicate) &&
(!object || qq.object.value === object) &&
(!graph || qq.graph.value === graph));
}
function removeQuads(quads: Quad[], subject?: string, predicate?: string, object?: string, graph?: string): Quad[] {
const filtered = getQuads(quads, subject, predicate, object, graph);
return quads.filter((qq): boolean => !filtered.includes(qq));
}
describe('A RepresentationMetadata', (): void => {
let metadata: RepresentationMetadata;
const identifier = namedNode('http://example.com/id');
const graphNode = namedNode('http://graph');
const inputQuads = [
quad(identifier, namedNode('has'), literal('data')),
quad(identifier, namedNode('has'), literal('moreData')),
quad(identifier, namedNode('hasOne'), literal('otherData')),
quad(identifier, namedNode('has'), literal('data'), graphNode),
quad(namedNode('otherNode'), namedNode('linksTo'), identifier),
quad(namedNode('otherNode'), namedNode('has'), literal('otherData')),
quad(namedNode('otherNode'), namedNode('graphData'), literal('otherData'), graphNode),
];
describe('constructor', (): void => {
@@ -87,8 +104,9 @@ describe('A RepresentationMetadata', (): void => {
});
it('can query quads.', async(): Promise<void> => {
expect(metadata.quads(null, namedNode('has'), null)).toHaveLength(3);
expect(metadata.quads(null, null, literal('otherData'))).toHaveLength(2);
expect(metadata.quads(null, namedNode('has'))).toHaveLength(getQuads(inputQuads, undefined, 'has').length);
expect(metadata.quads(null, null, literal('otherData')))
.toHaveLength(getQuads(inputQuads, undefined, undefined, 'otherData').length);
});
it('can change the stored identifier.', async(): Promise<void> => {
@@ -96,10 +114,10 @@ describe('A RepresentationMetadata', (): void => {
metadata.identifier = newIdentifier;
const newQuads = inputQuads.map((triple): Quad => {
if (triple.subject.equals(identifier)) {
return quad(newIdentifier, triple.predicate, triple.object);
return quad(newIdentifier, triple.predicate, triple.object, triple.graph);
}
if (triple.object.equals(identifier)) {
return quad(triple.subject, triple.predicate, newIdentifier);
return quad(triple.subject, triple.predicate, newIdentifier, triple.graph);
}
return triple;
});
@@ -129,6 +147,18 @@ describe('A RepresentationMetadata', (): void => {
expect(metadata.quads()).toBeRdfIsomorphic(expectedMetadata.quads());
});
it('can add a quad.', async(): Promise<void> => {
const newQuad = quad(namedNode('random'), namedNode('new'), literal('triple'));
metadata.addQuad('random', 'new', 'triple');
expect(metadata.quads()).toBeRdfIsomorphic(inputQuads.concat([ newQuad ]));
});
it('can add a quad with a graph.', async(): Promise<void> => {
const newQuad = quad(namedNode('random'), namedNode('new'), literal('triple'), namedNode('graph'));
metadata.addQuad('random', 'new', 'triple', 'graph');
expect(metadata.quads()).toBeRdfIsomorphic(inputQuads.concat([ newQuad ]));
});
it('can add quads.', async(): Promise<void> => {
const newQuads: Quad[] = [
quad(namedNode('random'), namedNode('new'), namedNode('triple')),
@@ -137,6 +167,18 @@ describe('A RepresentationMetadata', (): void => {
expect(metadata.quads()).toBeRdfIsomorphic([ ...newQuads, ...inputQuads ]);
});
it('can remove a quad.', async(): Promise<void> => {
const old = inputQuads[0];
metadata.removeQuad(old.subject as any, old.predicate as any, old.object as any, old.graph as any);
expect(metadata.quads()).toBeRdfIsomorphic(inputQuads.slice(1));
});
it('removes all matching triples if graph is undefined.', async(): Promise<void> => {
metadata.removeQuad(identifier, 'has', 'data');
expect(metadata.quads()).toHaveLength(inputQuads.length - 2);
expect(metadata.quads()).toBeRdfIsomorphic(removeQuads(inputQuads, identifier.value, 'has', 'data'));
});
it('can remove quads.', async(): Promise<void> => {
metadata.removeQuads([ inputQuads[0] ]);
expect(metadata.quads()).toBeRdfIsomorphic(inputQuads.slice(1));
@@ -164,30 +206,46 @@ describe('A RepresentationMetadata', (): void => {
});
it('can remove a single value for a predicate.', async(): Promise<void> => {
metadata.remove(inputQuads[0].predicate as NamedNode, inputQuads[0].object as Literal);
expect(metadata.quads()).toBeRdfIsomorphic(inputQuads.slice(1));
metadata.remove(namedNode('has'), literal('data'));
expect(metadata.quads()).toBeRdfIsomorphic(removeQuads(inputQuads, identifier.value, 'has', 'data'));
});
it('can remove single values as string.', async(): Promise<void> => {
metadata.remove(inputQuads[0].predicate as NamedNode, inputQuads[0].object.value);
expect(metadata.quads()).toBeRdfIsomorphic(inputQuads.slice(1));
metadata.remove(namedNode('has'), 'data');
expect(metadata.quads()).toBeRdfIsomorphic(removeQuads(inputQuads, identifier.value, 'has', 'data'));
});
it('can remove multiple values for a predicate.', async(): Promise<void> => {
metadata.remove(namedNode('has'), [ inputQuads[0].object, inputQuads[1].object ] as NamedNode[]);
expect(metadata.quads()).toBeRdfIsomorphic(inputQuads.slice(2));
metadata.remove(namedNode('has'), [ literal('data'), 'moreData' ]);
let expected = removeQuads(inputQuads, identifier.value, 'has', 'data');
expected = removeQuads(expected, identifier.value, 'has', 'moreData');
expect(metadata.quads()).toBeRdfIsomorphic(expected);
});
it('can remove all values for a predicate.', async(): Promise<void> => {
const pred = namedNode('has');
metadata.removeAll(pred);
const updatedNodes = inputQuads.filter((triple): boolean =>
!triple.subject.equals(identifier) || !triple.predicate.equals(pred));
expect(metadata.quads()).toBeRdfIsomorphic(updatedNodes);
expect(metadata.quads()).toBeRdfIsomorphic(removeQuads(inputQuads, identifier.value, 'has'));
});
it('can remove all values for a predicate in a specific graph.', async(): Promise<void> => {
const pred = namedNode('has');
metadata.removeAll(pred, graphNode);
expect(metadata.quads()).toBeRdfIsomorphic(
removeQuads(inputQuads, identifier.value, 'has', undefined, graphNode.value),
);
});
it('can get all values for a predicate.', async(): Promise<void> => {
expect(metadata.getAll(namedNode('has'))).toEqualRdfTermArray([ literal('data'), literal('moreData') ]);
expect(metadata.getAll(namedNode('has'))).toEqualRdfTermArray(
[ literal('data'), literal('moreData'), literal('data') ],
);
});
it('can get all values for a predicate in a graph.', async(): Promise<void> => {
expect(metadata.getAll(namedNode('has'), defaultGraph())).toEqualRdfTermArray(
[ literal('data'), literal('moreData') ],
);
});
it('can get the single value for a predicate.', async(): Promise<void> => {

View File

@@ -2,7 +2,7 @@ import 'jest-rdf';
import { literal, namedNode } from '@rdfjs/data-model';
import {
toCachedNamedNode,
toSubjectTerm,
toNamedTerm,
toPredicateTerm,
toObjectTerm,
toLiteral,
@@ -45,11 +45,11 @@ describe('TermUtil', (): void => {
describe('toSubjectTerm function', (): void => {
it('returns the input if it was a term.', async(): Promise<void> => {
const nn = namedNode('name');
expect(toSubjectTerm(nn)).toBe(nn);
expect(toNamedTerm(nn)).toBe(nn);
});
it('returns a named node when a string is used.', async(): Promise<void> => {
expect(toSubjectTerm('nn')).toEqualRdfTerm(namedNode('nn'));
expect(toNamedTerm('nn')).toEqualRdfTerm(namedNode('nn'));
});
});