Coder Social home page Coder Social logo

query-solid's Introduction

LDflex for Solid

Simple access to data in Solid pods through LDflex expressions

npm version Build Status Coverage Status Dependency Status

This library is a configuration of the LDflex language for the Solid ecosystem. It configures LDflex with:

  1. JSON-LD context for Solid
  2. a Solid-authenticated query engine (Comunica or rdflib.js)
  3. useful data paths for Solid

LDflex expressions occur for example on Solid React components, where they make it easy for developers to specify what data they want to show. They can also be used as an expression language in any other Solid project or framework.

Creating data paths

Once you obtain the solid.data object, start writing data paths from the following entry points.

The user entry point

The solid.data.user path can query data about the currently logged in user, such as:

  • solid.data.user.firstName yields the user's first name(s)
  • solid.data.user.email yields the user's email address(es)
  • solid.data.user.friends yields the user's friend(s)
  • solid.data.user.friends.firstName yields the user's friends' first name(s)

The any URL entry point

The solid.data[url] path can query data about any subject by URL, such as:

  • solid.data['https://ruben.verborgh.org/profile/#me'].firstName
  • solid.data['https://ruben.verborgh.org/profile/#me'].email
  • solid.data['https://ruben.verborgh.org/profile/#me'].friends
  • solid.data['https://ruben.verborgh.org/profile/#me'].friends.firstName

Specifying properties

As you can see in the above examples, an LDflex path starts with an entry point and is followed by property names, which can be:

  • abbreviations such as firstName (which expands to http://xmlns.com/foaf/0.1/givenName)
  • prefixed names such as foaf:givenName (which expands to http://xmlns.com/foaf/0.1/givenName)
  • full URLs such as http://xmlns.com/foaf/0.1/givenName

The abbreviations and prefixed names are expanded using a JSON-LD context. You can find some inspiration about what to ask for in this context.

You can access data using any vocabulary you want and, when included in the JSON-LD context, in multiple ways. For example:

  • FOAF:
    • solid.data.user.name
    • solid.data.user.foaf_name
    • solid.data.user.foaf$name
    • solid.data.user['foaf:name']
    • solid.data.user['http://xmlns.com/foaf/0.1/name']
  • vCard:
    • solid.data.user.vcard_fn
    • solid.data.user.vcard$fn
    • solid.data.user['vcard:fn']
    • solid.data.user['http://www.w3.org/2006/vcard/ns#fn']
  • Schema.org:
    • solid.data.user.schema_name
    • solid.data.user.schema$name
    • solid.data.user['schema:name']
    • solid.data.user['http://www.schema.org/name']
  • Custom:
    • solid.data.user['http://example.org/my-ontology/name']

The traditional colon syntax for prefixes (schema:name) can be substituted with an underscore (schema_name) or dollar sign (schema$name). This is because JavaScript keys with a colon require quotes (user['schema:name']) whereas underscores and dollar signs can be used freely (user.schema_name, user.schema$name).

Installation

npm install @solid/query-ldflex

Usage

Within Node.js environments

const { default: data } = require('@solid/query-ldflex');

const ruben = data['https://ruben.verborgh.org/profile/#me'];
showProfile(ruben);

async function showProfile(person) {
  const label = await person.label;
  console.log(`\nNAME: ${label}`);

  console.log('\nTYPES');
  for await (const type of person.type)
    console.log(`  - ${type}`);

  console.log('\nFRIENDS');
  for await (const name of person.friends.firstName)
    console.log(`  - ${name} is a friend`);
}

If, instead of Comunica, you want to use the rdflib.js query engine, install @ldflex/rdflib as a dependency of your project and use

const { default: data } = require('@solid/query-ldflex/lib/exports/rdflib');

When creating browser builds, it can be easier to simply tell webpack to replace @ldflex/comunica by @ldflex/rdflib.

In the browser

<script src="solid-auth-client.bundle.js"></script>
<script src="solid-query-ldflex.bundle.js"></script>
document.addEventListener('DOMContentLoaded', async () => {
  const user = solid.data.user;
  alert(`Welcome, ${await user.firstName}!`);
});

To replace Comunica by rdflib.js, opt for

<script src="solid-auth-client.bundle.js"></script>
<script src="rdflib.min.js"></script>
<script src="solid-query-ldflex.rdflib.js"></script>

Adding a custom JSON-LD context

In addition to the default properties, you might want to support your own:

console.log(solid.data.context);       // the raw default JSON-LD context
await solid.data.context.extend({      // add new JSON-LD context
  con: 'http://www.w3.org/2000/10/swap/pim/contact#',
  preferred: 'con:preferredURI',
});
console.log(await solid.data.context); // the expanded JSON-LD context

// Now we can use both existing and new properties
const ruben = solid.data['https://ruben.verborgh.org/profile/#me'];
console.log(await ruben.name);
console.log(await ruben.preferred);

Be aware though that this leads to expressions that are less portable, because they only work when the additional context is added.

Resolving string expressions

LDflex expressions are actual JavaScript—not strings. There are times when strings are more useful though, such as when building declarative components that take LDflex expressions.

The solid.data object exports a resolve interface that transforms a string expression into an actual LDflex path. This string is appended to solid.data to obtain the resulting path. For example:

  • solid.data.resolve('.user.firstName') becomes the path solid.data.user.firstName
  • solid.data.resolve('["https://example.org/"].label') becomes the path solid.data["https://example.org/"].label

For convenience, the starting dot and quotes inside of brackets can be omitted. If the path is a single URL, quotes and brackets can be omitted. The following strings will all resolve:

  • '.user.firstName'
  • 'user.firstName'
  • '["https://example.org/"].label'
  • '[https://example.org/].label'
  • 'https://example.org/'

License

©2018–present Ruben Verborgh, MIT License.

query-solid's People

Contributors

joachimvh avatar michielbdejong avatar rubensworks avatar rubenverborgh avatar yukulele avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

query-solid's Issues

Implement compatible DELETE for node-solid-server

LDflex is coded against the SPARQL UPDATE spec, but node-solid-server does not implement that specification correctly. Specifically, it will fail with a 409 if a value does not exist, or if there are multiple matches.

A workaround for this is that we wire .delete in @solid/query-ldflex with a different handler. This handler should:

  1. query all current values for the property
  2. send individual patches to delete every matched value

In theory, requests in 2 could happen in parallel, but NSS does not have locking, so concurrent requests would risk damaging the file.

e.on is not a function error in latest version

After upgrading to 2.8.0 (and ensuring I was on node v12) I started noticing an error in the console, seemingly one message per ldflex query. The error is:

Uncaught (in promise) TypeError: e.on is not a function
10LinkedRdfSourcesAsyncRdfIterator.js:91 Uncaught (in promise) TypeError: e.on is not a function

This was observed on multiple developer machines, and at least one server deployment. After rolling back to 2.6.0 the error went away. It did not appear to impede the actual queries from executing, but we rolled back anyway to prevent console errors.

Can't seem to get this to work in the browser

Hi,
I have a simple html page for now and I wanted to test logging in to a Solid pod and also retrieving the name from the pod to display "Hello Joe" or whatever.

I have added the two scripts to my html

<script src="solid-auth-client.bundle.js"></script>
<script src="solid-query-ldflex.bundle.js"></script>

I can log in and out.

but if I try this in the console,
await solid.data['https://mistermagoo.inrupt.net/profile/card#me'].name
I get the result undefined

If I try without the "card", I get an error
Error: Could not retrieve https://mistermagoo.inrupt.net/profile/ (401)

I think the syntax is correct, because I can enter this in the browser console
await solid.data['https://ruben.verborgh.org/profile/#me'].name
and get this result
h {id: ""Ruben Verborgh"@en", toString: ƒ, toPrimitive: ƒ}

I have tested this using your playground and that can read the name correctly, so it seems I am doing something wrong in my app.

Send PUT requests instead of PATCH requests

Unfortunately, PATCH requests are broken on node-solid server 4.x and 5.x in at least two ways:

  1. node-solid-server does not follow the SPARQL UPDATE spec when a DELETE clause when there are no or multiple matches (nodeSolidServer/node-solid-server#1085), so the client gets unexpected 409 responses.
  2. node-solid-server does not implement file locking (nodeSolidServer/node-solid-server#137), so accidentally concurrent patches can destroy a file, so the client gets unexpected 500 responses.

The first thing is more or less easily fixable, but deciding about the right fix is a longer process (linkeddata/rdflib.js#299). But even if we fix it, the client doesn't know whether it is talking to a fixed or a legacy server, and both require different approaches.

The second thing is hard to fix, as code for writing files is spread all over node-solid-server.

Hence, HTTP PATCH is currently not a reliable way forward. As such, I propose to:

  • apply the patch locally on the client, and update the remote resource through PUT
  • use the eTag mechanism to ensure we are not overwriting other people's changes

This solution is not ideal because it is inefficient for bandwidth, and will cause performance problems with larger files. However, it avoids file corruption, which is what we are currently risking, and it does not require changes in the client code (which was not broken in the first place).

TL;DR: PATCH is broken on the server, but we cannot easily fix it there; instead of forcing a workaround in clients, we can put that workaround in LDflex.

.properties should return compacted properties

When using the function properties on a resource or a container, I get a list of expanded properties (example)

Shouldn't it be compacted? It seems like LDFlex is doing it, and we already have the predicates function which returns expanded properties (example)

Ability to read and validate based on shapes

This is a bit of a pain point on one of my projects, the solid-react-sdk. We provide examples of how to do certain things, such as notification creation or file / game creation. In these cases we want to do two things:

  1. Fetch "objects" or object-like structures as defined by a shape (in our case, shex)
  2. Validate that these objects are what they say they are

Right now, these means a lot of common, repeated loops.

What we've done so far is create JSON structures representing a simple version of a shape. This isn't great practice, but you may see why we do this soon. We need to loop over something in order to create multiple ldflex expressions, one per property in the shape, then execute and combine together into one JS object.

These JSON structures are not good practice, since they are separate from the actual shapes, not used for validation, and are custom. That being said, it's more difficult to loop over ShEx or Shacl shapes and check each predicate name, for various reasons.

We also have to validate the data first - usually we do this with ShEx (though we have put together some Shacl experiments as well) and it's a bit of a pain as we have to fetch the data twice - the ShEx validator fetches once, as it takes a path as a param and does the fetch internally, and returns the valid paths, not the valid data. Then we use LDFlex to fetch the valid document URLs to build our objects as described above.

In a perfect world, I would like to be able to do something like

solid.data[subject].shape('<IRI to shape>') and have it return me a JS object contain the fields, or throw an error if it fails validation

In an even more perfect world I could do this with a list of subjects and get an array of JS objects that all pass validation :)

Underscores in property name prevents from getting the value

As you explain in the documentation, the semi-colon can be substituted by the underscores in property names.

In my example, I have a property name containing an underscore, and I get undefined when I try to fetch its value. Is it related?

const base_context = {
  '@vocab': 'http://happy-dev.fr/owl/#',
  rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
  ldp: 'http://www.w3.org/ns/ldp#',
  foaf: 'http://xmlns.com/foaf/0.1/',
  name: 'rdfs:label',
  acl: 'http://www.w3.org/ns/auth/acl#',
  permissions: 'acl:accessControl',
  mode: 'acl:mode',
};

await solid.data.context.extend(base_context);
const resource = solid.data['https://api.coopstarter.happy-dev.fr/resources/1/'];
console.log(`${await resource.publication_year}`); // Prints undefined

Browser performance issue on iterative property access

I face performances issues when accessing datas on a container. It makes all our apps not useable for now with LDFlex. For example, with the following code:

  await solid.data.context.extend(base_context)
  const container = solid.data["https://apiprod.happy-dev.fr/clients/"];
  console.time(container.toString())
  for await (const r of container.ldp_contains) {
    console.time(r.toString())
    console.log((await r.img).toString());
    console.timeEnd(r.toString())
  }
  console.timeEnd(container.toString())

It takes ~3000ms to display 70 times the img property of a resource.
For each resource, it takes between 10ms to 90ms to display its img.

Is there something we can do to improve this?

Clear cache to get edited values

Is there a way to clear the LDFlex cache?
In Startinblox, there are many situations where the user displays a value, edit it, and needs to display it again. As we don't reload pages, we always have the old values returned by LDFlex

Cannot display properties of some iterable resources

When I iterate on the resources of a container, I cannot get its properties. For example:

for await (let res of solid.data["https://api.test-paris.happy-dev.fr/sources/users/"].ldp_contains) {
  console.log(`${await res.type}`)
}
// shows "http://www.w3.org/ns/ldp#Container" twice, I expected "sib:source" twice

It appears that res.type is actually an AsyncIterator which returns all the types in a document:

for await (let res of solid.data["https://api.test-paris.happy-dev.fr/sources/users/"].ldp_contains) {
  for await (const type of res.type) {
    console.log(`${type}`);
  }
}
/* Result:
http://www.w3.org/ns/ldp#Container
3  http://www.w3.org/ns/auth/acl#Read
2  sib:source
http://www.w3.org/ns/ldp#Container
3  http://www.w3.org/ns/auth/acl#Read
2  sib:source
*/

Is the fact that there is no @id on these resources the source of the issue?

Should .user include the full "extended profile" data?

This is more of a proposal than an actual issue.

The Solid Data Discovery documentation describes the following process for discovering where a user's data you care about exists:

  1. Start with the WebID URI.
  2. Use it to fetch the WebID Profile Document.
  3. Parse the profile and load the other Extended Profile resources, which includes the Preferences file and any owl:sameAs and rdfs:seeAlso links.
  4. From the Extended Profile, extract the registry links (the solid:publicTypeIndex predicate link to the public Listed Type Index registry, and the solid:privateTypeIndex predicate link to the private Unlisted Type Index registry).
  5. Load one or both of the Type Index registry documents, as appropriate, and query them for the location of RDF Types that the app cares about.

However (assuming I'm groking things correctly), the user object that query-ldflex provides is only the data directly from the Profile Document, not including the "Extended" Profile data.

So, should query-ldflex populate the user object from the Extended Profile? In other words: Should user include step 3 in the above process?

Fetch an RDF list and return it as a Javascript array

It would be a very nice feature-add to be able to fetch an RDF collection such as:

<#_:_book_shex_BookDemo__parts_7> a ui:Classifier;
    ui:values ("Science Fiction" "Fantasy" "Romance" "Western" "Non-fiction" "Thriller" "Horror" "Douglas Adams").

... and return the values as a JS array. (This is real data I've used for demos and testing the SDK, using the Form Model syntax and vocabs).

Right now we have to run the results of values through a custom function that parses the collection recursively into an array by removing the rdf$first item, appending it to an array, then sending rdf$rest recursively into itself. The code snippet we use is here:

async function loopList(doc: any) {
  let parts: any = []

  const field = await doc.rdf$first
  if (field) {
    const nextField = await loopList(doc.rdf$rest)
    parts = [...parts, field.value, ...nextField]
  }

  return parts
}

This currently works and returns a collection as an array, but something like this seems extremely useful for managing other datatypes than just Literals.

resolve does not work with ":" syntaxes

Is it expected that, with this:

await solid.data.context.extend(base_context)
const resource = solid.data['https://api.coopstarter.happy-dev.fr/resources/1/']

This works:

console.log((await resource['rdf:type']).toString()); // "coopstarter:resource"
console.log((await resource.resolve('["rdf:type"]')).toString()); // "coopstarter:resource"
console.log((await resource.resolve('rdf_type')).toString()); // "coopstarter:resource"

But this does not:

console.log((await resource.resolve('rdf:type')).toString()); // Error

Create `from` path for arbitrary documents

Right now, we support:

  • data[subject] which will start a path for subject in the document with URL subject
  • data.from(source)[subject], which will start a path for subject in the RDF/JS store source

I propose to add another path:

  • data.from(document)[subject] (where document is a [promise to a string]), which will start a path for subject in the document with URL document

That essentially males data[subject] a shortcut for data.from(subject)[subject] (but that doesn't necessarily mean it should be implemented as such).

Closes #14.

Issue when try to create a blank node (id value) on document

Some of the documents might not have some of the common fields like role or hasAddress as part of the vcard document. IF the node is not a blank node, we can use add or set (ldflex functions). However, there are some cases where these nodes are blank nodes, so when we want to create a blank node (like id1223334445543) it doesn't work because it will save it as an string and shouldn't be saved as one but as a blank node

We tried to use this method: data.something.hasAddress.set(blankNode()) from https://github.com/rdfjs/data-model but it doesn't work as it will save an empty array instead of :id value.

CC: @joachimvh , @RubenVerborgh

Blank Node: the value of the node is an id that point to another object in the document

Enable support for reading/writing local data stores

Most projects will likely have local configuration options, such as i18n or projects settings, and we would like to store this in ttl if possible. Currently, though, we can't read in from a local file. We would like to enable local file support, so we can begin using local ttl files.

I've marked this as SDK 0.3.0, which will begin work very soon.

Get a property starting by '@' raises an error

If I try to get a property starting by @, like the JSONLD keywords, I get an error:

  await solid.data.context.extend(base_context)
  const type = solid.data["https://api.coopstarter.happy-dev.fr/resources/1/"]['@type']
  console.log(`${await type}`)

  // Uncaught (in promise) Error
  // expandProperty @ | JSONLDResolver.js:65

It works if I remove the @ before type. But in this case, I expect the http://happy-dev.fr/owl/#type property and not the rdf:type

how to externalize query-ldflex in shighl webpack.config.js ?

Hi @RubenVerborgh ,

I'm working on Shighl a S-olid HIGH L-evel API based on query-ldflex,
this is a tool with some high level function to interact with POD session, inbox, longchat and I need to externalize query-ldflex, but I don't know on to configure Webpack for that.

I've tried some

  externals: {
    'solid-auth-cli': 'null',
 '@solid/query-ldflex': 'query-ldflex',
    /*  '@solid/query-ldflex': {
    commonjs: 'query-ldflex',
    commonjs2: 'query-ldflex',
    amd: 'query-ldflex',
    root: 'data'
  },*/

but I got this error "
TypeError: _solid_query_ldflex__WEBPACK_IMPORTED_MODULE_20___default.a is undefined"

I can import vendor/solid-query-ldflex.bundle.js on this page https://scenaristeur.github.io/shighl/externalize.html but my lib is always ~10Mo.
Do you have an idea ? Thxs

Cannot remove @solid/context

My current context is:

{
  '@vocab': 'http://happy-dev.fr/owl/#',
  rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
  ldp: 'http://www.w3.org/ns/ldp#',
  foaf: 'http://xmlns.com/foaf/0.1/',
  name: 'rdfs:label',
  acl: 'http://www.w3.org/ns/auth/acl#',
  permissions: 'acl:accessControl',
  mode: 'acl:mode'
};

The @vocab defines our default which is used for almost all our properties. With the @solid context, some properties are overriden. For example, when we try to target http://happy-dev.fr/owl/#firstName, we get http://xmlns.com/foaf/0.1/givenName.

Is it possible to use only our context and not the solid one?

Fetch errors do not result in a promise rejection

When doing an asynchronous request, try/catch statements should properly catch errors, but they do not.

Example:

When running this code via a web-browswer on localhost, Tim's pod disallows requests coming from the unregistered localhost app. This is all fine, but the resulting error it triggers cannot be caught.

    try {
      const timName = (await data['http://www.w3.org/People/Berners-Lee/card#i'].vcard_fn).value;
      console.log(timName);
    } catch(err) {
      console.log('this should be caught')
      console.error(err);
    }

This yields Uncaught (in promise) TypeError: Failed to fetch

The same result will happen if I try to retrieve an improperly formatted ttl file

Unable to read prefs.ttl from web app

I'm unable to get any keys from the prefs.ttl file in my card, I'll elaborate with everything I'm doing.

  1. Fetching my card from my POD using LDFlex's data to be able to get my preferences file. (This works perfectly)

Key:

sp:preferencesFile </settings/prefs.ttl>;

Code:

const pttl = "http://www.w3.org/ns/pim/space#preferencesFile";
const { preferences } = ProfileShape;
const user = data[this.props.webId];
const preferencesFile =  await user[pttl]
  1. Fetching my preferences file using LDFlex's data and the file name I just got.
   const preferencesTtl = data[preferencesFile.value];

The file gets fetched correctly as a turtle file, I checked network tab and it worked.

@prefix : <#>.
@prefix dct: <http://purl.org/dc/terms/>.
@prefix c: </profile/card#>.
@prefix terms: <http://www.w3.org/ns/solid/terms#>.
@prefix n0: <http://xmlns.com/foaf/0.1/>.
@prefix sp: <http://www.w3.org/ns/pim/space#>.

c:me
    terms:privateTypeIndex <privateTypeIndex.ttl>;
    terms:publicTypeIndex <publicTypeIndex.ttl>;
    n0:language "en";
    n0:mbox <mailto:[email protected]>.
<> a sp:ConfigurationFile; dct:title "Preferences file".
  1. I tried and get the preferences keys (some different approaches I took)
const mbox =  await preferencesTtl['card:me']['foaf:mbox']
const mbox =  await preferencesTtl['foaf:mbox']

I might be approaching it in a wrong way or this might be an issue with LDFlex.

Add TypeScript Defintion

I started to create the type definition files for @solid/query-ldflex, ldflex-comunica and ldflex on a forked DefinitelyTyped repository.

https://github.com/FUUbi/DefinitelyTyped/tree/master/types/ldflex
https://github.com/FUUbi/DefinitelyTyped/tree/master/types/ldflex-comunica
https://github.com/FUUbi/DefinitelyTyped/tree/master/types/solid__query-ldflex/

I did not properly test the definitions, it is more the result of my working progress.
There are some types, which I was not too sure about what they return... I simply defined "any" as a type for those instances.

Would it be possible that you look over it? That would be great. And further are you ok if it would be published later on, by sending a pull request to the original DefinitlyTyped repository?

Add multi-doc example

The example in https://github.com/solid/query-ldflex/blob/master/demo/profile.js works inside one document. But if a profile only contains the foaf:knows triple, then the objects of those will need to be dereferenced before you can produce a list of first names of someone's friends. With @Vinnl, @james-martin-jd and myself we eventually worked out how to do that, in this chat conversation: https://gitter.im/solid/app-development?at=5d9de406b385bf2cc69f6c8d

Would be good to add an example of that (or maybe even better, a link to a tutorial) to the readme of this repo!

Error when try to get properties or subjects from document

When I try to get the properties from my webId I'm getting this error Error: The JSON-LD context cannot expand the 'properties' property

My code is the following:

    for await (let subject of user.properties) {
        console.log(subject)
    }

Using version 2.7.0 breaks my app's test suite

On my app, I was using 2.6.0 but recently tried upgrading to 2.7.0. This caused all test suites for pages using ldflex to fail with the following error message:

  appPath/node_modules/@solid/query-ldflex/node_modules/ldflex/lib/MutationFunctionHandler.js:78
            for await (const item of arg) objects.push(this.extractObject(pathData, path, item));
                ^^^^^

    SyntaxError: Unexpected reserved word

Support HTTP resources in the browser on an HTTPS page

I'm working on an application that needs to load vocabularies in order to fetch data, such as a label or a list of subClasses. This worked fine on localhost, but once deployed to a test server the data started failing to load. On investigation, we were getting a CORS issue as we were loading the vocabulary from https://develop.generator.inrupt.com but the vocabulary in question was vcard, which is http://www.w3.org/2006/vcard/ns#. I've used workarounds for this using .from in the past, but in this case the vocabularies are dynamic so it's hard to predict when to use the workaround. Is there a way to bake in a check and workaround? Thanks!

Allow blank nodes to be reused across expressions

Currently, a blank node resulting from one expression, cannot be reused in another. Following the example of https://github.com/solid/query-ldflex/issues/33, the following snippets return different results:

const alice = "https://drive.verborgh.org/public/2019/blanks.ttl#Alice";
for await (const name of solid.data[alice].friends.name)
  console.log(`${name}`)
const alice = "https://drive.verborgh.org/public/2019/blanks.ttl#Alice";
for await (const friend of solid.data[alice].friends)
  console.log(`${await friend.name}`)

This happens because Alice's friends are identified by blank nodes, and they lose context across multiple expressions. If we retry both snippets with Alice2, which has IRI friends, we get the same results.


We could strive to reuse blank nodes across expressions, by internally skolemizing them. Here is a sketch of how that could work:

  • When outputting blank nodes, Comunica assigns an internal identifier to them. For instance, _:b1 is still output as a BlankNode, but has a special internal field .skolemized that contains urn:skolem:1234.
  • When a SPARQL query is generated from such a skolemized blank node, the skolemized IRI is used instead of a blank node.
  • When returning results, any skolemized NamedNode is turned into a skolemized `BlankNode.

The key is inserting skolemization and deskolemization processing in the right place, for which I need to ask @rubensworks for help.

We could simply skolemize upon parsing, and then deskolemize right before results are returned. This works in all cases, except when Comunica directly operates on a store (the contents of which it did not parse, so it can contain actual blank nodes).

And alternative approach is a skolemizing store wrapper. It takes a store as an argument, and translates on the fly in its match etc. methods.

Perhaps both approaches can be used in conjunction: skolemization in parsers for all cases, except when a store is passed, then we wrap it.

Errors generated by ldflex are not available to parent application

When using LDFlex for reading or writing data, sometimes errors are (legitimately) generated, such as a 401 or 404 error when the resource is not available or the user doesn't have permission. The problem is the errors are not bubbled up to the parent application, so we cannot catch the error to handle it properly. Instead, the error is logged to the console and it silently fails.

For example, I would like to catch an error and display a friendly message in the UI to the user so they know something has gone wrong. In the past we've used solid-auth-client to do a fetch on the resource before using LDFlex, to ensure we have access and the resource exists. This isn't good practice though.

With the most recent 2.6.0 version, I have done a simple query, such as:

const podStoragePath = await data[webId].storage;

Sometimes the user's webId isn't accessible, or it's an invalid webId. I wrapped this code in a try/catch block, but the catch is never triggered.

Node setup instructions incomplete

I've tried to use this in a brand new app using node and my guess is there are some setup instructions missing. In particular, something about setting up the comunica engine. I've tried installing the @comunica/actor-rdf-source-identifier-hypermedia-qpf dependency as suggested by the error message, but that creates other issues.

I've reproduced the error in a hello world app here: https://codesandbox.io/s/solid-ldflex-hello-world-or9so?fontsize=14&module=%2Fsrc%2FApp.vue

Spec-compatible .set does not work with node-solid-server

Currently, if you try to use the .set function to edit a value, it performs a delete and an add, which is correct. However, if there are multiple values at the node you are deleting, a 409 error is returned from the server, since it does not know which value to delete.

Our proposed solution, working with @RubenVerborgh, is to update the set call so it will fetch all values at the requested node, then manually delete each one before inserting the new value.

Documentation needs updating

Didn't see an issue for this yet, so though I'd open one to track it. The documentation and README is currently very out of date, and doesn't cover some basic use cases. Notably missing is .set, .add, and .delete, as well as .from.

I think the rest of the docs should also be reviewed for accuracy and much has changed since it was written. Some kind of small tutorial / example code would also be very nice.

Retrieve a raw list as values without having to use for await

This might already exist, but I didn't see it documented and I was not able to find it while doing a brief amount of code exploration.

Essentially, I'd like to be able to get all values fetched as an array without using for await.... This way I can perform fetches for subsequent values in parallel.

    const friends = [];
    const ldFriends = (await data.user.friends).listValue; // <-- List value does not exist currently. Right now, calling value will return only one of the values retreived
    console.log(ldFriends);
    await Promise.all(ldFriends.map(async ldFriend => {
      console.log(ldFriend.value)
      try {
        friends.push({
          name: (await data[ldFriend.value].vcard_fn).value,
          webId: ldFriend.value
        })
      } catch(err) {}
    }))
    this.setState({ friends });

Enable JSON-LD expansion on subjects (in addition to properties)

As we sometimes use the resource ids in URLs, we would like to use reduced ids to make it cleaner.
I expected the following code to work but I get an error. Am I doing something wrong here?

const base_context = {
  ...
  "collective":"https://api.happy-dev.fr/collectives/"
};

await solid.data.context.extend(base_context);
const resource = solid.data['collective:1']
console.log(`${await resource.name}`)

Unable to create new web access control resources

Unable to create new web access control resources

Full featured write to a solid pod means the ability to manipulate web access control rules, adding, updating, and removing wac resources as needed. In the current node solid server, we are unable to create new acl files. However, if ldflex is going to work across solid implementations, it shouldn’t be married to the underlying implementation (in this case files), otherwise you have to change the code if you switch to a different solid server that doesn’t use file based ACL.

Example in Readme on node

I was trying to test LDFlex by following the example instructions in the Readme and created the following program:

const { PathFactory } = require('ldflex');
const { default: ComunicaEngine } = require('ldflex-comunica');
const { namedNode } = require('@rdfjs/data-model');

// The JSON-LD context for resolving properties
const context = {
  "@context": {
    "@vocab": "http://xmlns.com/foaf/0.1/",
    "friends": "knows",
    "label": "http://www.w3.org/2000/01/rdf-schema#label",
  }
};
// The query engine and its source
const queryEngine = new ComunicaEngine('https://ruben.verborgh.org/profile/');
// The object that can create new paths
const path = new PathFactory({ context, queryEngine });

const ruben = path.create({ subject: namedNode('https://ruben.verborgh.org/profile/#me') });
showPerson(ruben);

async function showPerson(person) {
  console.log(`This person is ${await person.name}`);
}

When executing it in node (version v11.12.0), it returns:

> node testReadme.js 
(node:7097) UnhandledPromiseRejectionWarning: ReferenceError: window is not defined
    at currentUrl (/home/labra/src/cursos/ldflextest/node_modules/solid-auth-client/lib/url-util.js:9:42)
    at toUrlString (/home/labra/src/cursos/ldflextest/node_modules/solid-auth-client/lib/url-util.js:32:23)
    at SolidAuthClient.fetch (/home/labra/src/cursos/ldflextest/node_modules/solid-auth-client/lib/solid-auth-client.js:41:51)
    at ActorHttpSolidAuthFetch.run (/home/labra/src/cursos/ldflextest/node_modules/@comunica/actor-http-solid-auth-fetch/lib/ActorHttpSolidAuthFetch.js:13:22)
    at ActorHttpSolidAuthFetch.runObservable (/home/labra/src/cursos/ldflextest/node_modules/@comunica/core/lib/Actor.js:53:29)
    at MediatorNumber.mediate (/home/labra/src/cursos/ldflextest/node_modules/@comunica/core/lib/Mediator.js:80:22)
(node:7097) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 91)
(node:7097) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Should it work or is there something missing?

I created this simple repository in case you want to reproduce it: https://github.com/cursosLabra/ldflextest

How to process transformations on a container

For the needs of startinblox, we need to process some transformations on a container to sort, filter, group... resources.

To do this, we recreate an array of resources from the AsyncIterator returned by query-ldflex to perform the transformations.
We have two main concerns with this approach:

  • it's complicated to use native javascript functions on the arrays like sort, filter because of the asynchronous proxies
  • it's probably not a good practice to recreate an object from an already existing one.

Is there a smarter way to handle this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.