Skip to content

Commit

Permalink
draft CRUD API
Browse files Browse the repository at this point in the history
  • Loading branch information
dherault committed Dec 2, 2016
1 parent 6dc1023 commit 2514900
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 2 deletions.
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
# blazegraph-js
Blazegraph JavaScript API
# Blazegraph-js

*[Blazegraph](https://www.blazegraph.com/) JavaScript API*

# Note

Unstable, do not use in production!

The repo won't be public until the first production-ready release.

# LICENSE

Blazegraph-js is released under the MIT license.

[Blazegraph](https://www.blazegraph.com/) is freely available under the GPLv2 open-source license.
204 changes: 204 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/* eslint-disable no-shadow */
const request = require('request');
const createRdfParser = require('n3').Parser;
const { host, port, namespace } = require('../config');

const blazegraphUrl = `http://${host}:${port}/blazegraph/namespace/${namespace}/sparql`;
const nquadsMimeType = 'text/x-nquads';

// const headers = { 'Content-Type': 'text/x-nquads' };

const parser = createRdfParser({ format: 'N-Quads' });

/* ----------
UTILS
---------- */

// Simple promise wrapper around the 'request' library
function makeRequest(options) {
return new Promise((resolve, reject) => {
request(options, (error, response, body) => (error || response.statusCode !== 200 ? reject : resolve)(body || error));
});
}

// Validation functions
function validateInput(input) {
if (!(input && typeof input === 'object')) throw new Error('Malformed input');

return input;
}

function isNonEmptyString(value) {
return value && typeof value === 'string';
}

// URI encodes a SPARQL query
function encodeQuery(query) {
return encodeURIComponent(query.replace('\n', ' ').replace('\t', ''));
}

// URI encodes a quad (complete or not)
function encodeQuad({ subject, predicate, object, graph }) {
let url = '';

if (isNonEmptyString(subject)) url += `&s=${encodeURIComponent(subject)}`;
if (isNonEmptyString(predicate)) url += `&p=${encodeURIComponent(predicate)}`;
if (isNonEmptyString(object)) url += `&o=${encodeURIComponent(object)}`;
if (isNonEmptyString(graph)) url += `&c=${encodeURIComponent(graph)}`;

return url.slice(1);
}

// Serializes a quad into the nquad format (has to be complete)
// NOTE: this middleware enforces the usage of named graph on every triple
function serializeQuad({ subject, predicate, object, graph }) {

if (!(isNonEmptyString(subject) && isNonEmptyString(predicate) && isNonEmptyString(object) && isNonEmptyString(graph))) {
throw new Error(`Malformed input: ${JSON.stringify({ subject, predicate, object, graph }, null, 2)}`);
}

return `${subject} ${predicate} ${object} ${graph} .`;
}

/* ---------------
MIDDLEWARE
--------------- */

/*
Read all quads matching a pattern
{
subject?: <IRI>
predicate?: <IRI>
object?: <IRI> or "Literal"
graph?: <IRI>
graphs?: [<IRI>]
}
*/
function readQuads(input) {
validateInput(input);

let fullUrl = `${blazegraphUrl}?GETSTMTS&includeInferred=false&${encodeQuad(input)}`;

if (Array.isArray(input.graphs)) input.graphs.forEach(g => fullUrl += `&c=${g}`);

return makeRequest(fullUrl)
.then(nquads => new Promise((resolve, reject) => {
const quads = [];

parser.parse(nquads, (error, triple) => {
if (error) return reject(error);
if (triple) return quads.push(triple);

resolve(quads);
});
}));
}

/*
Create one or more quads
{
subject: <IRI>
predicate: <IRI>
object: <IRI> or "Literal"
graph: <IRI>
}
Input can also be an array of quads
*/
function createQuads(input) {
return makeRequest({
url: blazegraphUrl,
method: 'POST',
headers: {
'Content-Type': nquadsMimeType,
},
body: (Array.isArray(input) ? input : [input])
.map(quad => serializeQuad(validateInput(quad)))
.join('\n'),
});
}

/*
Update a quad knowing its old statement
{
subject: <IRI>
predicate: <IRI>
object: <IRI> or "Literal", from the old statement
newObject: <IRI> or "Literal", defines the new statement
graph: <IRI>
}
*/
function updateQuad(input) {
validateInput(input);

const oldQuad = serializeQuad(input);
const newQuad = serializeQuad(Object.assign({}, input, { object: input.newObject }));
const options = { contentType: nquadsMimeType };

return makeRequest({
url: `${blazegraphUrl}?updatePost`,
method: 'POST',
formData: {
remove: {
options,
value: oldQuad,
},
add: {
options,
value: newQuad,
},
},
});
}

// Perform a SPARQL update query
// NOTE: this does not allow to perform any SPARQL query
function updateSparql(query) {
if (!isNonEmptyString(query)) throw new Error('Query must be a non-empty string');

return makeRequest({
url: `${blazegraphUrl}?update=${encodeQuery(query)}`,
method: 'POST',
});
}

/*
Delete all quads matching a pattern
{
subject?: <IRI>
predicate?: <IRI>
object?: <IRI> or "Literal"
graph?: <IRI>
}
*/
function deleteQuads(input) {
validateInput(input);

const params = encodeQuad(input);

if (!params) throw new Error('You almost deleted the whole database!');

return makeRequest({
url: `${blazegraphUrl}?${params}`,
method: 'DELETE',
});
}

// Delete statements using a SPARQL CONSTRUCT or DESCRIBE query
// NOTE: this does not allow to perform any SPARQL query
function deleteSparql(query) {
if (!isNonEmptyString(query)) throw new Error('Query must be a non-empty string');

return makeRequest({
url: `${blazegraphUrl}?query=${encodeQuery(query)}`,
method: 'DELETE',
});
}

module.exports = {
readQuads,
createQuads,
updateQuad,
updateSparql,
deleteQuads,
deleteSparql,
};
23 changes: 23 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "blazegraph",
"version": "0.0.0",
"description": "Blazegraph JavaScript API",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nelson-ai/blazegraph-js.git"
},
"author": "Nelson a.i.",
"license": "MIT",
"bugs": {
"url": "https://github.com/nelson-ai/blazegraph-js/issues"
},
"homepage": "https://github.com/nelson-ai/blazegraph-js#readme",
"dependencies": {
"n3": "^0.8.3",
"request": "^2.79.0"
}
}

0 comments on commit 2514900

Please sign in to comment.