A minimal SEPA client for browser and nodejs environments. Note : this library is in an early development stage, use at your own risk.
Installation
npm i @arces-wot/sepa-js
or in your html document
<script src="https://cdn.jsdelivr.net/npm/@arces-wot/sepa-js/web/sepa.js"/>
Usage
SEPA-js comes with basic api to interact with the engine (Core API). But it also provides a rich interface to create Dynamic Linked Data applications with the support of JSON Semantic Application Profile (JSAP).
Core API
Nodejs:
const sepa = require('@arces-wot/sepa-js').client
Browser:
const sepa = Sepajs.client
The variable presented above returns a pre-configured sepa client instace. The following constructor can be used to have more control about defaults and protocol paramenter.
const SEPA = require('@arces-wot/sepa-js').SEPA
let client = new SEPA({/*...config...*/})
the following is the list of the parameters that can be set in a SEPA client instance:
{
"host": "localhost",
"oauth": {
"register": "https://localhost:8443/oauth/register",
"tokenRequest": "https://localhost:8443/oauth/token"
},
"sparql11protocol": {
"protocol": "http",
"port": 8000,
"query": {
"path": "/query",
"method": "POST",
"format": "JSON"
},
"update": {
"path": "/update",
"method": "POST",
"format": "JSON"
}
},
"sparql11seprotocol": {
"protocol": "ws",
"availableProtocols": {
"ws": {
"port": 9000,
"path": "/subscribe"
},
"wss": {
"port": 9443,
"path": "/secure/subscribe"
}
}
},
"options" : {
"httpsAgent" : httpsAgent,
"headers" : {
"fancy-header" : "content"
}
}
}
Refer to official documentation for details of protocol parameters, while options field can be configured with the same properties defined in axios configuration schema.
Subscribe
The subscribe primitive allows a client to receive notifications about a query result. More information about notification data format can be found here. This function as all the others can accept a configuration object as second argument. The object specifies particular parameters that are valid only for this function call. The returned object is a subscription which can be revoked using the unsubscribe function.
const sub = sepa.subscribe("select * where{?sub ?obj ?pred}LIMIT 1",{host:"www.vaimee.com"})
sub.on("subscribed",console.log)
sub.on("notification", not => {
console.log("Notifcation:");
console.log(JSON.stringify(not, null, 2));
sub.unsubscribe()
})
sub.on("error",console.error)
Publish
The update function sends the SPARQL 1.1 update given as the first argument. For further information about response format see here. Notice that also this function accepts a configuration object as second argument.
sepa.update("insert {<hello> <from> 'js'}where{}", {host:"www.vaimee.com"})
.then(()=>{console.log("Updated");})
Query
For polling functionalities the query primitive issues a SPARQL 1.1 query to the endpoint. To know more about response data format refer to the official documentation. The last argument of this function is the configuartion object used to specific protocol parameters that are valid only for this function call.
sepa.query("select * where {?s ?p 'js'}", {host:"www.vaimee.com"})
.then((data)=>{console.log("SPARQL bindings: " + data);})
Security
Core api supports secure connection with the endpoint. After obtaining clientID
and clientSecret
pair, a secure client can be instatieted with:
const SecSEPA = require('@arces-wot/sepa-js').client.secure
const secClient = new SecSEPA(clientID,clientSecret)
If your SEPA instance supports the register
primitive you can use the corrisponding function in sepa-js.
const register = require('@arces-wot/sepa-js').client.secure.register
register("SEPATest").then(sClient =>{
return sclient.query("select * where {?s ?p 'js'}")
})
Secure client has the same interface of the unsecure one please refer to Core API for details.
Certificate
SEPA-js client ships with the default certificate published on SEPA repository, if you have a custom instance of SEPA engine you should configure the SEPA certificate in your secure client as following:
const https = require('https')
// Your certificate
const ca = `
Bag Attributes
friendlyName: sepakey
localKeyID: 54 69 6D 65 20 31 35 35 33 32 34 39 38 34 33 33 34 36
subject=/C=IT/ST=Bologna/L=Bologna/CN=localhost
issuer=/C=IT/ST=Bologna/L=Bologna/CN=localhost
-----BEGIN CERTIFICATE-----
MIIDKTCCAhGgAwIBAgIEbMARRzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJJ
VDEQMA4GA1UECBMHQm9sb2duYTEQMA4GA1UEBxMHQm9sb2duYTESMBAGA1UEAxMJ
bG9jYWxob3N0MB4XDTE5MDMyMjEwMDg1M1oXDTIwMDMxNjEwMDg1M1owRTELMAkG
A1UEBhMCSVQxEDAOBgNVBAgTB0JvbG9nbmExEDAOBgNVBAcTB0JvbG9nbmExEjAQ
BgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AJZ9ajuqEqXGU5QAWyMG3w4hScec9BXjOwDeqseSDDxOx/KuHCG5JDTiQzmPBT96
LLUGTn+c2l2c+Ezm4Dk11qjpJ+aiiv+gGvyNJmpw/+UwW7wp13O9sMr21GZxexmZ
/xV/nsXFXXoUurCwZecTzQ6UcNvrvlUy7NVr2TU+ZpwWR/DyKhxe452VJlEaP9Yk
Zu/g9x9rYMs7iG4qErQnGhGS6ds8fU6VrzPCAW9EFxC1SN7r2xnPREU/igv0SilX
e+tPk167l1wgGyBl3K4Ep7O8RoSE/EhyuooZ9oVenqD/MRlKLso1O2Kv7DslrBpv
8UufAWxwwEOhDA3XXfblsDkCAwEAAaMhMB8wHQYDVR0OBBYEFCxDyoFe03ccASA9
JfyvYFEAStceMA0GCSqGSIb3DQEBCwUAA4IBAQBvWzC0qLNhp8L9GkoaNtNKJGEu
WQkqaMDBtrD4Jy+I75/k73ivvwVgbgg0kq9+jYC48tWwcBsDzqNau+Zay4rWZlf9
qbnP3+j4hgLIBPrAAvxWQBzLrVOkZK1hXdrS1fNCFmYdIwlEU7M06C3mv69CD/yJ
vJF2FczexVR2I2L15JdpVlqZ35KwQ8QRTKTtwvQxZeZG56g+Db0vGMMwJqSpPRZc
WdUXV+2aTVZWdO3avHXkS/qZ0A+8HX8bVvm8O/5b21bIo9BfCf3za3/CAVSNFfNp
VfDUVhC465CzJcei94rxKyjWTuVl7CZA+6e2x5Ua/4tASi0sFFAlqGJIpiXr
-----END CERTIFICATE-----
`
const httpsAgent = new https.Agent({ ca: [ca] })
const SecSEPA = require('@arces-wot/sepa-js').secure
const secClient = new SecSEPA(clientID,clientSecret,{ options : {
httpsAgent : httpsAgent
}})
// Or using register function
const register = require('@arces-wot/sepa-js').client.secure.register
register("SEPATest",{ options : { httpsAgent : httpsAgent}}).then(sClient =>{
return sclient.query("select * where {?s ?p 'js'}")
})
Query bench api
From v0.10.0 SEPAjs provides apis to store query templates and substitutes variables. See the following example:
const Bench = require('sepajs').bench
bench = new Bench()
query = bench.sparql("select * where{?a ?b ?c.}",{
a:{
type: "uri",
value :"urn:epc:id:gid:0.1.0102030405060708090A0B0C"
}
})
// query : select * where{<urn:epc:id:gid:0.1.0102030405060708090A0B0C> ?b ?c}
for futher details check query bench unit tests here.
Note: Inside broswers the bench api can be required with const bench = new Sepajs.bench();
as for other Sepajs functionalities.
JSAP api
Jons Sparql Application Profile. JASAP api leverage on Query Bench API to provide an application development model.
Nodejs:
const App = require('sepa-js').Jsap
Browser:
const JsapApi = Sepajs.Jsap
app = new JsapApi({
host: "mml.arces.unibo.it",
queries : {
simpleQuery : { sparql : "select * where {?a ?b ?c}"}
}
})
let subscription = app.simpleQuery({})
subscription.on("notification",console.log)
JSAP object example:
jsap_example = {
host: "mml.arces.unibo.it",
oauth: {
enable : false,
register: "https://localhost:8443/oauth/register",
tokenRequest: "https://localhost:8443/oauth/token"
},
sparql11protocol: {
protocol: "http",
port: 8000,
query: {
path: "/query",
method: "POST",
format: "JSON"
},
update: {
path: "/update",
method: "POST",
format: "JSON"
}
},
sparql11seprotocol: {
protocol: "ws",
availableProtocols: {
ws: {
port: 9000,
path: "/subscribe"
},
wss: {
port: 9443,
path: "/secure/subscribe"
}
}
},
namespaces: {
exp: "http://www.w3.org/example#",
},
updates: {
simpleUpdate: {
sparql: "INSERT DATA { exp:hello exp:from 'js' }"
},
updateArgs: {
sparql: "INSERT DATA {?sub ?pred ?obj}",
forcedBindings: {
sub: {
type: "uri",
value: "exp:hello"
},
pred: {
type: "uri",
value: "exp:from"
},
obj: {
type: "literal",
value: "js"
}
}
}
},
queries : {
simpleQuery : {
sparql : "select * where{?a ?b ?c}"
},
queryArgs : {
sparql : "select * where{?a ?b ?c}",
forcedBindings : {
a : {
type : "uri",
value : "exp:subj"
}
}
}
}
}
Subscriber
subscriber = new JsapApi(jsap_example)
let sub = subscriber.simpleQuery({})
sub.on("notification",console.log)
Publisher
publisher = new JsapApi(jsap_example)
publisher.simpleUpdate().then(res=>{console.log("Update response: " + res)})
Forced bindings
The JSAP api support query bindings to easly inject data in query templates. Here is an example to use a producer with code specifed bindings:
app = new JsapApi(jsap_example)
data = {
sub : "exp:person1",
pred: "exp:hasName",
obj : "Max"
}
app.updateArgs(data).then(res=>{console.log("Update response: " + res)})
The SPARQL update issued to the broker will be:
PREFIX exp:<http://www.w3.org/example#>
INSERT DATA {exp:person1 exp:hasName 'Max'}
Note with JSAP you can specify default arguments and their types
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate.
Note: run tests with npm run tests
and if succefull npm run integration-test
. Additionally, integration tests needs a default SEPA instance
running on your local machine. Refer to SEPA github page for information about the installation and configuration.