Elda#
Elda is declarative: the API is an RDF document in the Linked Data API vocabulary. Each operation declares a graph pattern and property paths, which Elda turns into queries. It binds one endpoint per API and cannot express a join across independent result sets, so it answers from Meta only. It ships pluggable formatters for several serialisations.
from helper import call
A simple request#
Looking up a DOI returns the article from OpenCitations Meta as a Linked Data API response.
call("http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w")
curl 'http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w'
# 200
{
"format": "linked-data-api",
"version": "0.2",
"result": {
"_about": "http://localhost:8088/oc/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"definition": "http://localhost:8088/oc/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"extendedMetadataVersion": "http://localhost:8088/oc/articles.json?_metadata=all&doi=10.1007%2Fs11192-022-04367-w",
"first": "http://localhost:8088/oc/articles.json?_page=0&doi=10.1007%2Fs11192-022-04367-w",
"hasPart": "http://localhost:8088/oc/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"isPartOf": "http://localhost:8088/oc/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"items": [
{
"_about": "https://w3id.org/oc/meta/br/061202127149",
"contributorship": [
{
"_about": "https://w3id.org/oc/meta/ar/061209588721",
"agent": {
"_about": "https://w3id.org/oc/meta/ra/061206532419",
"familyName": "Cioffi",
"givenName": "Alessia"
}
},
{
"_about": "https://w3id.org/oc/meta/ar/061209588722",
"agent": {
"_about": "https://w3id.org/oc/meta/ra/061206532420",
"familyName": "Coppini",
"givenName": "Sara"
}
},
{
"_about": "https://w3id.org/oc/meta/ar/061209588723",
"agent": {
"_about": "https://w3id.org/oc/meta/ra/06250110138",
"familyName": "Massari",
"givenName": "Arcangelo"
}
},
{
"_about": "https://w3id.org/oc/meta/ar/061209588724",
... (98 more lines)
The join#
No join. Elda binds one endpoint per API and cannot express a join across independent result sets, so it cannot reach OpenCitations Index to add the reference count.
Output#
Built-ins cover JSON, JSON-LD, RDF/XML, Turtle, XML, HTML, and Atom. elda/xslt/skg.xsl adds a minimal SKG-IF view wired in elda/oc.ttl as the skg format.
call("http://localhost:8088/oc/articles.ttl?doi=10.1007/s11192-022-04367-w")
curl 'http://localhost:8088/oc/articles.ttl?doi=10.1007/s11192-022-04367-w'
# 200
PREFIX api: <http://purl.org/linked-data/api/vocab#>
PREFIX datacite: <http://purl.org/spar/datacite/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX frbr: <http://purl.org/vocab/frbr/core#>
PREFIX literal: <http://www.essepuntato.it/2010/06/literalreification/>
PREFIX pro: <http://purl.org/spar/pro/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
<https://w3id.org/oc/meta/ra/06250110138>
foaf:familyName "Massari";
foaf:givenName "Arcangelo" .
<https://w3id.org/oc/meta/id/06590555219>
literal:hasLiteralValue "S148561398" .
<https://w3id.org/oc/meta/ra/061206532419>
foaf:familyName "Cioffi";
foaf:givenName "Alessia" .
<https://w3id.org/oc/meta/ar/061209588726>
pro:isHeldBy <https://w3id.org/oc/meta/ra/067099715> .
<https://w3id.org/oc/meta/ra/061206532421>
foaf:familyName "Moretti";
foaf:givenName "Arianna" .
<https://w3id.org/oc/meta/id/062603621>
literal:hasLiteralValue "1588-2861" .
<https://w3id.org/oc/meta/ar/061209588721>
pro:isHeldBy <https://w3id.org/oc/meta/ra/061206532419> .
<https://w3id.org/oc/meta/ra/0614010840729>
foaf:familyName "Peroni";
foaf:givenName "Silvio" .
<https://w3id.org/oc/meta/ar/061209588724>
pro:isHeldBy <https://w3id.org/oc/meta/ra/061206532421> .
... (68 more lines)
call("http://localhost:8088/oc/articles.skg?doi=10.1007/s11192-022-04367-w")
curl 'http://localhost:8088/oc/articles.skg?doi=10.1007/s11192-022-04367-w'
# 200
{
"products": [
{
"local_identifier": "https://w3id.org/oc/meta/br/061202127149",
"entity_type": "product",
"title": "Identifying And Correcting Invalid Citations Due To DOI Errors In Crossref Data",
"contributors": [
{
"given_name": "Alessia",
"family_name": "Cioffi"
},
{
"given_name": "Sara",
"family_name": "Coppini"
},
{
"given_name": "Arcangelo",
"family_name": "Massari"
},
{
"given_name": "Arianna",
"family_name": "Moretti"
},
{
"given_name": "Silvio",
"family_name": "Peroni"
},
{
"given_name": "Cristian",
"family_name": "Santini"
},
{
"given_name": "Nooshin",
"family_name": "Shahidzadeh"
}
],
"venue": {
"name": "Scientometrics",
"issn": [
"1588-2861",
... (6 more lines)
Pagination#
call("http://localhost:8088/oc/authors?article=https://w3id.org/oc/meta/br/061202127149&_page=0&_pageSize=2")
curl 'http://localhost:8088/oc/authors?article=https://w3id.org/oc/meta/br/061202127149&_page=0&_pageSize=2'
# 200
{
"format": "linked-data-api",
"version": "0.2",
"result": {
"_about": "http://localhost:8088/oc/authors.json?_pageSize=2&_page=0&article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"definition": {
"_about": "http://localhost:8088/oc/authors.json?article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"definition": "http://localhost:8088/oc/authors.json?article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"hasPart": "http://localhost:8088/oc/authors.json?_pageSize=2&_page=0&article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"type": "http://purl.org/linked-data/api/vocab#ListEndpoint"
},
"extendedMetadataVersion": "http://localhost:8088/oc/authors.json?_pageSize=2&_page=0&_metadata=all&article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"first": "http://localhost:8088/oc/authors.json?_pageSize=2&_page=0&article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"isPartOf": "http://localhost:8088/oc/authors.json?article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"items": [
"https://w3id.org/oc/meta/ra/067099715",
"https://w3id.org/oc/meta/ra/061206532419"
],
"itemsPerPage": 2,
"next": "http://localhost:8088/oc/authors.json?_pageSize=2&_page=1&article=https%3A%2F%2Fw3id.org%2Foc%2Fmeta%2Fbr%2F061202127149",
"page": 0,
"startIndex": 1,
"totalResults": 7,
"type": "http://purl.org/linked-data/api/vocab#Page"
}
}
Versioning#
Not supported.
API description#
No OpenAPI or Swagger. The interface is described by the LDA metadata.
call("http://localhost:8088/oc/articles.ttl?doi=10.1007/s11192-022-04367-w&_metadata=all")
curl 'http://localhost:8088/oc/articles.ttl?doi=10.1007/s11192-022-04367-w&_metadata=all'
# 200
PREFIX api: <http://purl.org/linked-data/api/vocab#>
PREFIX datacite: <http://purl.org/spar/datacite/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX elda: <http://www.epimorphics.com/vocabularies/lda#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX frbr: <http://purl.org/vocab/frbr/core#>
PREFIX literal: <http://www.essepuntato.it/2010/06/literalreification/>
PREFIX pro: <http://purl.org/spar/pro/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
<https://w3id.org/oc/meta/ra/06250110138>
foaf:familyName "Massari";
foaf:givenName "Arcangelo" .
<https://w3id.org/oc/meta/id/06590555219>
literal:hasLiteralValue "S148561398" .
<https://w3id.org/oc/meta/ra/061206532419>
foaf:familyName "Cioffi";
foaf:givenName "Alessia" .
<https://w3id.org/oc/meta/ar/061209588726>
pro:isHeldBy <https://w3id.org/oc/meta/ra/067099715> .
<https://w3id.org/oc/meta/ra/061206532421>
foaf:familyName "Moretti";
foaf:givenName "Arianna" .
<https://w3id.org/oc/meta/id/062603621>
literal:hasLiteralValue "1588-2861" .
<https://w3id.org/oc/meta/ar/061209588721>
pro:isHeldBy <https://w3id.org/oc/meta/ra/061206532419> .
<https://w3id.org/oc/meta/ra/0614010840729>
foaf:familyName "Peroni";
foaf:givenName "Silvio" .
... (376 more lines)
Consumer authentication#
Elda reads only, and its authentication protects the upstream endpoint, not the API’s consumers.
Endpoint authentication#
HTTP Basic. The specification gives the endpoint an elda:authKey, and Elda reads the user and the password from the file with that name in /etc/elda/conf.d/ROOT/.
call("http://localhost:8088/oc/basic/articles?doi=10.1007/s11192-022-04367-w", max_lines=20)
curl 'http://localhost:8088/oc/basic/articles?doi=10.1007/s11192-022-04367-w'
# 200
{
"format": "linked-data-api",
"version": "0.2",
"result": {
"_about": "http://localhost:8088/oc/basic/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"definition": "http://localhost:8088/oc/basic/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"extendedMetadataVersion": "http://localhost:8088/oc/basic/articles.json?_metadata=all&doi=10.1007%2Fs11192-022-04367-w",
"first": "http://localhost:8088/oc/basic/articles.json?_page=0&doi=10.1007%2Fs11192-022-04367-w",
"hasPart": "http://localhost:8088/oc/basic/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"isPartOf": "http://localhost:8088/oc/basic/articles.json?doi=10.1007%2Fs11192-022-04367-w",
"items": [
{
"_about": "https://w3id.org/oc/meta/br/061202127149",
"contributorship": [
{
"_about": "https://w3id.org/oc/meta/ar/061209588721",
"agent": {
"_about": "https://w3id.org/oc/meta/ra/061206532419",
"familyName": "Cioffi",
"givenName": "Alessia"
... (117 more lines)
Caching#
Per-endpoint, configured with elda:cacheExpiryTime. The cache state, with hit and miss counts and the perma-cache/limit-entries/limit-triples policies, is visible at a control endpoint.
call("http://localhost:8088/control/show-cache")
curl http://localhost:8088/control/show-cache
# 200
<html><head></head><body style='background-color: #ccffcc'>
<h1>Elda cache state</h1>
<form method='POST' action='reset-counts'><input type='SUBMIT' value='RESET COUNTS'></form>
<form method='POST' action='clear-cache'><input type='SUBMIT' value='CLEAR CACHE'></form>
<h1>cache state</h1>
<h2>details for cache group 'default'</h2>
#1.SparqlSource{http://meta-counted:7001/sparql; unauthenticated} (select 0, view 0 entries)<div style='margin-top: 1ex'>SELECT: 0.0s since first, 0 requests, 0 hits, 0 misses, 0 drops (last 0.0s ago).</div>
<div style='margin-top: 1ex'>VIEW: 0.0s since first, 0 requests, 0 hits, 0 misses, 0 drops (last 0.0s ago).</div>
<div style='margin-top: 1ex'>COUNTS: 0.0s since first, 5 requests, 3 hits, 2 misses, 0 drops (last 0.0s ago).</div>
<div style='margin-top: 1ex'>RESULT: 0.0s since first, 5 requests, 3 hits, 2 misses, 0 drops (last 0.0s ago).</div>
#2.SparqlSource{http://meta-basic:3030/sparql; basic authentication} (select 0, view 0 entries)<div style='margin-top: 1ex'>SELECT: 0.0s since first, 0 requests, 0 hits, 0 misses, 0 drops (last 0.0s ago).</div>
<div style='margin-top: 1ex'>VIEW: 0.0s since first, 0 requests, 0 hits, 0 misses, 0 drops (last 0.0s ago).</div>
<div style='margin-top: 1ex'>COUNTS: 0.0s since first, 0 requests, 0 hits, 0 misses, 0 drops (last 0.0s ago).</div>
<div style='margin-top: 1ex'>RESULT: 0.0s since first, 1 requests, 0 hits, 1 misses, 0 drops (last 0.0s ago).</div>
<h2>details for cache group 'limit-entries'</h2>
<h2>details for cache group 'limit-triples'</h2>
<h2>details for cache group 'perma-cache'</h2>
</body></html>
Elda reaches OpenCitations Meta through a proxy that records every request it forwards. Once the control endpoint has emptied the cache, the first call reaches the endpoint, while the second one is answered from the cache.
import requests
from helper import count_endpoint_requests
requests.get("http://localhost:8088/control/clear-cache", timeout=120)
count_endpoint_requests("http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w")
First call: 200 , SPARQL requests that reached the endpoint: 3
Second call: 200 , SPARQL requests that reached the endpoint: 0
Operations#
GET only. The other methods are rejected.
call("http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w", method="POST", max_lines=0)
call("http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w", method="PUT", max_lines=0)
call("http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w", method="DELETE", max_lines=0)
curl -X POST 'http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w'
# 405
... (1 more lines)
curl -X PUT 'http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w'
# 405
... (1 more lines)
curl -X DELETE 'http://localhost:8088/oc/articles?doi=10.1007/s11192-022-04367-w'
# 405
... (1 more lines)