|
|
|
# Synopsis
|
|
|
|
|
|
|
|
Linked Data Platform (LDP), provides a set of rules for HTTP operations on web resources to provide an architecture for read-write Linked Data on the web. In short, federation is the connecting of multiple server instances so that the service provided to the user is consistent across multiple peers and databases, potentially using different technologies and structures
|
|
|
|
|
|
|
|
# The Federated Model
|
|
|
|
|
|
|
|
DjangoLDP is built on the `Model` class from the [main package](https://git.startinblox.com/djangoldp-packages/djangoldp)
|
|
|
|
|
|
|
|
```python
|
|
|
|
class Model(models.Model):
|
|
|
|
urlid = LDPUrlField(blank=True, null=True, unique=True)
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
|
|
|
In Linked Data Protocol, every resource can be uniquely identified by a url. In DjangoLDP, this accessible on every model by its `urlid` field. A clientside application will take the urlid, and use it to access the underlying resource it points to
|
|
|
|
|
|
|
|
In a federated application, I expect to use instances both **local** (with a urlid belonging a resource on my server) and **external** (with a urlid pointing to a resource on another server)
|
|
|
|
|
|
|
|
Detailed documentation on the options and methods available on this model are available from the package page
|
|
|
|
|
|
|
|
# Client-Side Federation
|
|
|
|
|
|
|
|
A federated client is a client which serves and interfaces data from multiple sources. Using Solid standards, we're able to use a single protocol to talk to any of these servers, so that APIs and clients are no longer inter-dependent
|
|
|
|
|
|
|
|
# Server-Side Federation
|
|
|
|
|
|
|
|
Server-side, federation is achieved in consistency of data links between peers. This means that if a local object references a model with an external `urlid` (a local pointer to a distant resource), a reverse pointer needs to be created on the distant server and maintained (for example if an object in the relation is deleted or changed)
|
|
|
|
|
|
|
|
These reverse-pointers are also referred to as **backlinks**. A backlinks service is provided in djangoldp/activities, which listens to `post_save`, `post_delete` and `m2m_changed` signals on all Models, utilising the [ActivityStreams](https://www.w3.org/TR/activitystreams-vocabulary/) vocabulary to inform referenced peers of the need to update their references
|
|
|
|
|
|
|
|
## The Backlinks System in DjangoLDP
|
|
|
|
|
|
|
|
Take, for example, that we have the following models:
|
|
|
|
|
|
|
|
```python
|
|
|
|
from django.contrib.auth import AbstractUser
|
|
|
|
from djangoldp.models import Model
|
|
|
|
|
|
|
|
class User(Model, AbstractUser):
|
|
|
|
...
|
|
|
|
|
|
|
|
class Circle(Model):
|
|
|
|
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="circles")
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that both models inherit from `djangoldp.models.Model`, where `Circle` has a ForeignKey to `User`
|
|
|
|
|
|
|
|
Managing federated users requires a little extra effort in authentication which isn't included in this tutorial, for more information please see [the associated guide](https://git.startinblox.com/djangoldp-packages/djangoldp/wikis/guides/authentication)
|
|
|
|
|
|
|
|
### Backlink Detection
|
|
|
|
|
|
|
|
In the djangoldp/activities app, there are several signal handlers which listen for an object being saved, deleted or a many-to-many relation changed. When an object is saved or deleted, the relations of its model are parsed, identifying any foreign key relations which reference an external urlid. For example:
|
|
|
|
|
|
|
|
```python
|
|
|
|
user = User.objects.create(urlid='https://distant.com/users/1/')
|
|
|
|
Circle.objects.create(owner=user)
|
|
|
|
```
|
|
|
|
|
|
|
|
In this code I have created a link to an external user, which I have then made the owner to my local circle. When the Circle object is saved, the distant link will be identified, its `inbox` discovered (see the [Linked Data Notifications](https://www.w3.org/TR/ldn/) spec for details) and the following activity sent to it:
|
|
|
|
|
|
|
|
```python
|
|
|
|
{
|
|
|
|
"@context": [...],
|
|
|
|
"summary": "A circle was created",
|
|
|
|
"type": "Create",
|
|
|
|
"actor": {
|
|
|
|
"type": "Service",
|
|
|
|
"name": "Backlinks Service"
|
|
|
|
},
|
|
|
|
"object": {
|
|
|
|
"@type": "hd:circle",
|
|
|
|
"@id": "https://local.com/circles/1/",
|
|
|
|
"owner": {
|
|
|
|
"@type": "foaf:user",
|
|
|
|
"@id": "https://distant.com/users/1/"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Similarly this service will manage saves for `Update`/`Delete` activities on a model, and `Add`/`Remove` activities for many-to-many relationships
|
|
|
|
|
|
|
|
### Responding to Backlinks
|
|
|
|
|
|
|
|
DjangoLDP defines an `InboxView` which is capable of receiving ActivityStreams format requests. Upon successfully parsing an activity, it returns `201 - Accepted` with the URL of the stored activity in the `Location` header of the response
|
|
|
|
|
|
|
|
In the case of our prior example, the backlink is processed by parsing the object, saving backlinks for any distant resources (or getting an existing reference) and updating the relationship. The model will be marked with `is_backlink = True` to indicate its status
|
|
|
|
|
|
|
|
### Other consumers
|
|
|
|
|
|
|
|
In certain cases, the currently linked references are not the only servers which I should notify of an object's status. Consider that in our example another service may wish to listen to the object for changes
|
|
|
|
|
|
|
|
For these purposes DjangoLDP defines a `Follower` model, which simply stores the `object` URL being watched and the `inbox` subscribing to it. As a third-party consumer I can send a `Follow` activity on the object:
|
|
|
|
|
|
|
|
```python
|
|
|
|
{
|
|
|
|
"@context": [...],
|
|
|
|
...
|
|
|
|
"type": "Follow",
|
|
|
|
"actor": {
|
|
|
|
"type": "Person",
|
|
|
|
"name": "Alice",
|
|
|
|
"inbox": "https://alice.com/inbox/"
|
|
|
|
},
|
|
|
|
"object": {
|
|
|
|
"@type": "hd:circle",
|
|
|
|
"@id": "https://distant.com/circles/1/"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Which will cause me to be notified of any Updates to the object's relations until it is deleted, or I revoke my subscription with an `Undo` activity |
|
|
\ No newline at end of file |