@treecg/versionawareldesinldp
TypeScript icon, indicating that this package has built-in type declarations

0.3.0 • Public • Published

VersionAwareLDESinLDP

npm DOI

The goal of this repository is to interact with an LDES in LDP with simple CRUD (Create, Read, Update and Read) operations.

This library has been tested with the Community Solid Server (CSS).

How does it work

The class VersionAwareLDESinLDP is an implementation of dealing with a versioned time-based LDES in LDP.

It provides functions to initialise a versioned LDES in LDP and create, read, update and delete* a member in the LDES in LDP using the version identifier. Functions:

  • create(versionID, store) appends the contents of the N3 store to the LDES in LDP.
    • also checks whether the versionID exists already. If it does, will throw an error
  • update(versionID, store) appends the contents of the N3 store to the LDES in LDP.
  • read(versionID) returns the most recent member with that version identifier.
  • delete(versionID)uses the read method as member and adds a triple that marks the resource as deleted.
  • extractVersions(VersionID) extracts the different versions using the version identifier within the LDES in LDP

*With deleting is meant that the resource is just marked as delete and not actually removed from the LDES (remember that all members of an LDES are immutable)

The class LDESinLDP is an implementation of the LDES in LDP protocol. This class is used within VersionAwareLDESinLDP and provides some utilities.

The three main features initialising, appending a member and creating a new fragment are implemented through their respective function initialise, append and newFragment. However, adding the version triple and time triple to append a member to the LDES in LDP is not done in the append function.

Using the library

Set up

First install the package

# install package
npm i @treecg/versionawareldesinldp
# (Optionally) set up a solid server
npx @solid/community-server -p 3000 -f ./data -c "@css:config/file-no-setup.json"
# or one without file system
npx @solid/community-server -c test/util/memory-no-setup.json

Initialising an LDES in LDP

const {LDPCommunication, LDESinLDP, VersionAwareLDESinLDP} = require('@treecg/versionawareldesinldp');
const ldesinldpIdentifier = 'http://localhost:3000/ldesinldp/'; // Base URL of the LDES in LDP 
const communication = new LDPCommunication();
const ldesinldp = new LDESinLDP(ldesinldpIdentifier, communication);
const versionAware = new VersionAwareLDESinLDP(ldesinldp);

// initialise
await versionAware.initialise()

From this point, this initialised LDES in LDP will be used through versionAware in the next code examples unless stated otherwise.

What is created?

Click here
- localhost:3000/
 |- ldesinldp/
  |- {timestamp}/

In the container at URL http://localhost:3000/, the ldesinldp is created. Furthermore, the ldes is described in there in that container. Which can be verified by sending a GET request

curl http://localhost:3000/ldesinldp/
@prefix dc: <http://purl.org/dc/terms/> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix tree: <https://w3id.org/tree#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .


<http://localhost:3000/ldesinldp/> dc:modified "2022-09-29T13:23:30.000Z"^^xsd:dateTime ;
    rdf:type ldp:BasicContainer , ldp:Container , ldp:Resource , tree:Node ;
    ldp:contains <http://localhost:3000/ldesinldp/1664457810373/> ;
    ldp:inbox <http://localhost:3000/ldesinldp/1664457810373/> ;
    tree:relation [
        rdf:type tree:GreaterThanOrEqualToRelation ;
        tree:node <http://localhost:3000/ldesinldp/1664457810373/> ;
        tree:path dc:created ;
        tree:value "2022-09-29T13:23:30.373Z"^^xsd:dateTime
    ] .
    tree:viewDescription <http://localhost:3000/ldesinldp/#ViewDescription> .

<http://localhost:3000/ldesinldp/#EventStream> rdf:type <https://w3id.org/ldes#EventStream> ;
    ldes:timestampPath dc:created ;
    ldes:versionOfPath dc:isVersionOf ;
    tree:view <http://localhost:3000/ldesinldp/> .
    
<http://localhost:3000/ldesinldp/#ViewDescription> rdf:type tree:ViewDescription ;
    dcat:endpointURL <http://localhost:3000/ldesinldp/> ;
    dcat:servesDataset <http://localhost:3000/ldesinldp/#EventStream> ;
    ldes:managedBy <http://localhost:3000/ldesinldp/#LDESinLDPClient> .

<http://localhost:3000/ldesinldp/#LDESinLDPClient> rdf:type ldes:LDESinLDPClient ;
    ldes:bucketizeStrategy <http://localhost:3000/ldesinldp/#BucketizeStrategy> .
    
<http://localhost:3000/ldesinldp/#BucketizeStrategy> rdf:type ldes:BucketizeStrategy ;
    ldes:bucketType ldes:timestampFragmentation ;
    tree:path dc:created .

Create a version object

I want to store a resource (following triple:resourcev1 http://purl.org/dc/terms/title "Title" . ) to the LDES in LDP and want to later read it with an identifier (http://example.org/resource1).

This is done with the function create which allows to append a new version object to the LDES in LDP.

const {Store, DataFactory} = require("n3");
const namedNode = DataFactory.namedNode;
const literal = DataFactory.literal;
const store = new Store();

const memberIdentifier = '#resource'; // could also be a full IRI e.g. http://example.org/resource1v1 
const versionID = 'http://example.org/resource1';
store.addQuad(namedNode(memberIdentifier), namedNode('http://purl.org/dc/terms/title'), literal('Title'));
await versionAware.create(versionID, store, memberIdentifier);

Read a version object

I want to read the resource that has been written.

The function read extracts the latest version of the version object and returns it as an N3 Store.

const {storeToString} = require('@treecg/versionawareldesinldp'); // utility function to convert a Store to a string

const resource = await versionAware.read(versionID);
console.log(storeToString(resource))

Which outputs

<http://localhost:3000/ldesinldp/1664457810373/f982b7f3-c1e6-4a9a-8c0c-f7f40e98f638#resource> <http://purl.org/dc/terms/title> "Title" .
<http://localhost:3000/ldesinldp/1664457810373/f982b7f3-c1e6-4a9a-8c0c-f7f40e98f638#resource> <http://purl.org/dc/terms/isVersionOf> <http://example.org/resource1> .
<http://localhost:3000/ldesinldp/1664457810373/f982b7f3-c1e6-4a9a-8c0c-f7f40e98f638#resource> <http://purl.org/dc/terms/created> "2022-09-29T13:33:16.932Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .

Update a version object

I want to change the title of my resource to Fancy Title.

The function update stores a new version of this version object to the LDES in LDP.

store.addQuad(namedNode(memberIdentifier), namedNode('http://purl.org/dc/terms/title'), literal('Fancy Title'));
await versionAware.update(versionID, store, memberIdentifier);

Delete a version object

The function delete marks a members as deleted. For this, it copies the latest version object and adds a triple to indicate this deletion.

await versionAware.delete(versionID);

Reading all the version objects

The function extractVersions retrieves all members with a given version identifier.

const options = {
    amount:Infinity, // I want all members
    chronologically: true // the members are sorted from oldest to newest
} // It is also possible retrieve all members within a window by giving a `startDate` and `endDate` argument

// extractVersions extracts all members with a given version identifier constrained by the options
const resources = await versionAware.extractVersions(versionID, options)

for (const resource of resources) {
    console.log(storeToString(new Store(resource.quads)))
}

In the output, the three different version objects can clearly be seen:

  1. We created a resource with a title: "Title"
  2. Later, this resource its contents are completely rewritten with as result that it now has a title: "Fancy Title"
  3. Finally with the triple <...#resource> a ldes:DeletedLDPResource, we've marked this resource as deleted.
<http://localhost:3000/ldesinldp/1664457810373/f982b7f3-c1e6-4a9a-8c0c-f7f40e98f638#resource> <http://purl.org/dc/terms/title> "Title" .
<http://localhost:3000/ldesinldp/1664457810373/f982b7f3-c1e6-4a9a-8c0c-f7f40e98f638#resource> <http://purl.org/dc/terms/isVersionOf> <http://example.org/resource1> .
<http://localhost:3000/ldesinldp/1664457810373/f982b7f3-c1e6-4a9a-8c0c-f7f40e98f638#resource> <http://purl.org/dc/terms/created> "2022-09-29T13:33:16.932Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .

<http://localhost:3000/ldesinldp/1664457810373/5f8119f6-649c-4fea-8b4a-daf979e1a362#resource> <http://purl.org/dc/terms/title> "Fancy Title" .
<http://localhost:3000/ldesinldp/1664457810373/5f8119f6-649c-4fea-8b4a-daf979e1a362#resource> <http://purl.org/dc/terms/isVersionOf> <http://example.org/resource1> .
<http://localhost:3000/ldesinldp/1664457810373/5f8119f6-649c-4fea-8b4a-daf979e1a362#resource> <http://purl.org/dc/terms/created> "2022-09-29T14:08:58.602Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .

<http://localhost:3000/ldesinldp/1664457810373/4bc6fa4d-7b99-41aa-a289-ba3823223045#resource> <http://purl.org/dc/terms/title> "Fancy Title" .
<http://localhost:3000/ldesinldp/1664457810373/4bc6fa4d-7b99-41aa-a289-ba3823223045#resource> <http://purl.org/dc/terms/isVersionOf> <http://example.org/resource1> .
<http://localhost:3000/ldesinldp/1664457810373/4bc6fa4d-7b99-41aa-a289-ba3823223045#resource> <http://purl.org/dc/terms/created> "2022-09-29T14:08:58.690Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
<http://localhost:3000/ldesinldp/1664457810373/4bc6fa4d-7b99-41aa-a289-ba3823223045#resource> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/ldes#DeletedLDPResource> .

Authenticated LDES in LDP

A private LDES in LDP can be created by using SolidCommunication and a Session (from solid-client-authn-js ).

const {SolidCommunication, LDESinLDP, VersionAwareLDESinLDP} = require('@treecg/versionawareldesinldp');
let session: Session; // Get a login session (@inrupt/solid-client-authn-node or @inrupt/solid-client-authn-browser)
const ldesinldpIdentifier = 'http://localhost:3000/ldesinldp/'; // Base URL of the LDES in LDP 
const communication = new SolidCommunication(session);
const ldesinldp = new LDESinLDP(ldesinldpIdentifier, communication);
const versionAware = new VersionAwareLDESinLDP(ldesinldp);

Getting a Session

The following code can be used to retrieve a Session if you are working with javascript node.

const {login, isLoggedin, getSession} = require('@treecg/versionawareldesinldp')

const validatedOptions = {
    applicationName: "LDES-orchestrator",
    registrationType: "dynamic",
    solidIdentityProvider: "http://localhost:3000"
};

await login(validatedOptions);
await isLoggedin(); // code that checks whether you are already logged in
const session = await getSession();

Feedback and questions

Do not hesitate to report a bug.

Further questions can also be asked to Wout Slabbinck (developer and maintainer of this repository).

Dependencies (20)

Dev Dependencies (12)

Package Sidebar

Install

npm i @treecg/versionawareldesinldp

Weekly Downloads

0

Version

0.3.0

License

ISC

Unpacked Size

381 kB

Total Files

87

Last publish

Collaborators

  • ajuvercr
  • woutslabbinck
  • ddvlanck
  • dexagod
  • pietercolpaert
  • hdlva
  • julianrojas87
  • brechtvdv
  • kasperzutterman