Commit edfe3132 authored by Laurent Wouters's avatar Laurent Wouters
Browse files

[doc] Added documentation

parent ccc4ace4a4b2
# CubicWeb Linked Data Browser
This is the developer-oriented documentation for the CubicWeb Linked Data Browser.
* [Writing new views](WritingNewViews.md)
* [Linked data server compliance](ServerLDCompliance.md)
# Linked Data Server Compliance
The [CubicWeb Linked Data Browser]() is a browser to navigate the web of data.
The point of this document is not to completely explain the web of data; but it is important to important to understand one of its underlying promise:
Namely that data or resources can be referred to by URIs and that by connecting to those URIs, interpreted as locations, the data associated with the URIs can be retrieved, potentially through content negotiation.
For example, a RDF resource (file) refers to entities identified by URIs.
The promise is then that the data(sets) about these entities can be fetched at the URIs.
Therefore, in the same way that a browser for the web of documents can navigate from pages to pages through links using URLs,
a browser for the web of (linked) data could navigate within and between datasets through the URIs of the entities referred to and find new datasets about them.
For this ideal scenario, the web servers that in the end serve the content, in this case the data, should *play nice*, i.e. be compliant with the web of data.
In this context, this mainly mean that when asked about an URI in its scope, a web server should answer with data about the requested URI, at least if explicitely asked to using HTTP headers related to content negotiation.
For example, a compliant server of linked data handling resources for `example.com` should readily (or through content negotiation) answers with the data related to `http://example.com/some_resource`.
Example of compliance issues:
* The server always answers HTML, or redirect to an HTML page, despite content negotiation requesting RDF data.
* Possible fix: Rely on content negotiation to answer with RDF content. HTML can still be returned by default.
* Instead of replying with data, the server redirects to `http://example.com/some_resource.rdf`.
* Possible dix: Do not redirect to different URIs depending on RDF syntaxes (.n3, .nt, .rdf, etc.) but directly return the content in the appropriate syntax, as requested through content negotiation.
* The datasets returned by the server make use of multiple writings for the same logical entity, for example using unicode escape sequences `\uxxxx` in URIs.
* Possible fix: Try to canonicalize data to a single URI writing.
* Multiple datasets have been located for different writing of the same URI and they have different content.
* The provided datasets use language specific URIs with different data for different languages.
* Possible fix: Unify the datasets in a language-neutral naming scheme for the entities and use language-tag on language-specific RDF literals.
The [CubicWeb Linked Data Browser]() still tries to cooperate with servers that are not already compliant.
It does the following to try to detect datasets:
* Simple content negotiation by requesting RDF data at the URI for the resource (case for compliant servers).
* Follow redirections to URIs for datasets serialized in specific syntaxes.
* Try to detect datasets linked to from HTTP `Link` header.
* When HTML was obtained, try to detect datasets linked to from HTML `Link` headers.
* When HTML was obtained, try to detect links to corresponding datasets, i.e. links starting with the URI of the requested resource and ending with a file extension for a known RDF syntax (.n3, .nt, .rdf, etc.).
\ No newline at end of file
# Writing new views for CubicWeb Linked Data Browser
The [CubicWeb Linked Data Browser]() relies on views to render RDF datasets.
By default, it only provides a single view that simply lists the RDF triples from the loaded dataset.
All other views are user-defined and dynamically discovered and loaded by the browser.
This document explains the basics of writing new views.
At its core, a view is simply a piece of Javascipt code (or any language that compiles to it) that implements a simple interface.
There are a few other requirements, as listed below:
* The view implementation must conform to the [`ViewImplementation`]() interface.
* The Javascript resource containing the implementation must contain an entrypoint for the browser.
* The view implementation must be referred to by a endpoint serving a descriptor of the implementation.
This document go through all these requirements and explain how to fulfill them.
In the end, the implementation of a view does not require any special behavior from the server it.
They are simply static files.
## Step 1 - Implement `ViewImplementation`
A library has been designed to help at the implementation of views.
It is available on npm as `@logilab/libview`.
This library is also used by the browser and therefore provides all the necessary APIs and interfaces.
First, add this library as a dependency in your project:
```sh
npm install @logilab/libview
```
The next step is to start the implementation of the main interface to provide: `ViewImplementation`.
This interface is located in the `implementation` namespace within `@logilab/libview`.
A good starting point is (in Typescript):
```ts
import { application, definition, implementation } from "@logilab/libview";
import * as $rdf from "rdflib";
class MyView implements implementation.ViewImplementation {
descriptor: definition.ViewDescriptor;
constructor() {
this.descriptor = ...;
}
priorityFor(store: $rdf.Formula, target: application.Resource): number {
return 0;
}
render(
renderer: application.ViewRenderer,
context: application.RenderingContext,
target: application.Resource
): implementation.ViewRendering {
return ...;
}
}
```
This interface is basically three members:
* `descriptor` is a public attribute that must contain the descriptor (metadata for the view).
* `priorityFor` is a method that, given a RDF dataset and a target resource (URI) to be rendered, must return the self-evaluated priority of the view.
This value is used by the browser to select an appropriate view for the dataset.
A higher (positive) number indicates a higher priority.
A negative number can be return to indicate that the view *must not* be selected by the browser.
As of this writing, the browser primarily relies on these self-evaluation by the views, unless the user explicitely select a particular view.
* `render` is the single entrypoint for the view implementation used by the browser. Given a rendered, a contact and a target resource (URI) to be rendered, it must return a rendering that may or may not include an HTML DOM sub-tree.
The basic idea for a view implementation is for the browser to specify the RDF dataset and the main resource to render and for the view to provide an HTML DOM tree.
How the DOM tree is produced is left to the discretion of the view implementation.
Any framework may be used, such as React, Angular, Vue, etc.; or no framework at all.
### Descriptor
The view descriptor is an object that conform the `ViewDescriptor` interface.
It *must* have the following fields:
* `identifier`, a unique identifier for the view.
* `name`, a short human-readable name for the view.
* `description`, a more lengthier description for the view. It may expands of the kind of RDF data that the view is able to handle.
* `entrypoint`, the name of an exported variable that contains an instance of the view implemention. See Step 2.
* `resourceCss`, an array of potentially remote CSS resources that should be loaded by the browser for this view.
* `resourceJs`, an array of potentially remote additional Javascript resources that should be loaded by the browser for this view.
* `resourceMain`, points to the potentially remote Javascript resource that contains the view implementation.
Example:
```ts
const MY_VIEW_DESCRIPTOR: definition.ViewDescriptor = {
identifier: "::SeriousBusiness::MyView",
name: "A short human-readable name",
description: "A lengthier description of this view. What kind of RDF data does it handle?",
entrypoint: "VIEW_ENTRYPOINT", // name of the entrypoint object
resourceCss: [
{
location: "remote",
uri:
"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
}
],
resourceJs: [],
resourceMain: {
location: "remote",
uri: "http://views.example.com/myview.js"
}
};
```
### Priority evaluation
The RDF dataset.
The target RDF resource.
### Rendering
The renderer.
The rendering context.
The resulting rendering.
### Facilities to manipulate RDF data
RDF entities and entity store.
Load objects from RDF data.
## Step 2 - Entrypoint
When the resource (file) in which a view implementation is defined is loaded by the browser, it needs to known what is the main object that represents the actual view implementation.
This entity is the entrypoint.
It usually is a simple constant that holds an instance of the view implementation object that implements the `ViewImplementation` interface.
Continuing on the example, the entrypoint may be defined as follow:
```ts
export const VIEW_ENTRYPOINT: MyView = new MyView();
```
The name of the entrypoint, `VIEW_ENTRYPOINT` in the excerpt above, *must* be the same as the name given in the view descriptor.
## Step 3 - Serve the view
The last step consists in setting-up a JSON file containing a list of one or more view descriptors.
The descriptors in the file *must be* identical to the ones provided by the view implementations themselves.
For example:
```json
[
{
"identifier": "::SeriousBusiness::MyView",
"name": "A short human-readable name",
"description": "A lengthier description of this view. What kind of RDF data does it handle?",
"entrypoint": "VIEW_ENTRYPOINT",
"resourceCss": [
{
"location": "remote",
"uri": "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
}
],
"resourceJs": [],
"resourceMain": {
"location": "remote",
"uri": "http://views.example.com/myview.js"
}
},
]
```
In this descriptor, the uri provided for the main resource *must* point to the Javascript file that contains the view implementation.
The JSON file can then be served and the browser can points to it as a source of view so that the implementations can be loaded.
The `@logilab/libview` library contains a ready-to-use `index.html` that can be used as a companion to the JSON file to provide a more human-readable landing page for users.
A working setup would be to serve the following directory at an URI like `http://views.example.com/`:
```
<root>
+-> index.html copied from @logilab/libview
+-> index.vd.json JSON file that contains the view descriptors
+-> myview.js Javascript resource containing the implemenation of MyView
```
The process of copying the `index.html` file can be automated, for example by using webpack in `webpack.config.js`:
```js
module.exports = [
{
plugins: [
new CopyWebpackPlugin(
[
{ from: "src/index.vd.json", to: "index.vd.json" },
{
from: "node_modules/@logilab/libview/build/index.html",
to: "index.html"
}
],
{}
)
]
}
];
```
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment