semantic-graphql
Advanced tools
Comparing version 0.5.1 to 0.6.0
# Changelog | ||
# 0.5.1 | ||
## 0.6.0 | ||
**Breaking changes:** | ||
- The names of generated GraphQL types are now capitalized regardless of the terminology. | ||
**New features:** | ||
- `resolvers.resolveSourceTypes` can now return a Promise. | ||
**Miscellaneous:** | ||
- `SemanticGraph#toString` now displays the number of triples in the graph. | ||
- Triples with predicates not used by the lib are not stored in memory. | ||
- Improved documentation. | ||
- Improved tests. | ||
## 0.5.1 | ||
**Bug fixes:** | ||
- Bug on `owl:inverseOf` inference that broke the feature in some cases. | ||
# 0.5.0 | ||
## 0.5.0 | ||
@@ -22,5 +36,5 @@ **Breaking changes:** | ||
- Tests! :tada: (very basic for now) | ||
- Better docs | ||
- Improved documentation. | ||
# 0.4.0 | ||
## 0.4.0 | ||
@@ -37,3 +51,3 @@ **Breaking changes:** | ||
# 0.3.1 | ||
## 0.3.1 | ||
@@ -43,3 +57,3 @@ **Bug fixes:** | ||
# 0.3.0 | ||
## 0.3.0 | ||
@@ -52,3 +66,3 @@ **Breaking changes:** | ||
# 0.2.1 | ||
## 0.2.1 | ||
@@ -58,3 +72,3 @@ **Bug fixes:** | ||
# 0.2.0 | ||
## 0.2.0 | ||
@@ -81,4 +95,4 @@ **Breaking changes:** | ||
# 0.1.0 | ||
## 0.1.0 | ||
First release! :tada: |
{ | ||
"name": "semantic-graphql", | ||
"version": "0.5.1", | ||
"description": "Create GraphQL schemas from RDF-based ontologies", | ||
"version": "0.6.0", | ||
"description": "Create GraphQL schemas from RDF ontologies", | ||
"author": "David Hérault <dherault@nelson.ai> (https://github.com/dherault)", | ||
"license": "MIT", | ||
"main": "index.js", | ||
"author": "David Hérault <david.herault@nelson.ai> (https://github.com/dherault)", | ||
"license": "MIT", | ||
"repository": { | ||
@@ -17,5 +17,7 @@ "type": "git", | ||
"engines": { | ||
"node": ">=6.0.0" | ||
"node" : ">=6.9.1" | ||
}, | ||
"//prepublish": "./scripts/checkgit.sh && npm test && npm run build", | ||
"options": { | ||
"mocha": "--bail --check-leaks" | ||
}, | ||
"scripts": { | ||
@@ -28,12 +30,9 @@ "test": "./node_modules/.bin/mocha $npm_package_options_mocha", | ||
}, | ||
"options": { | ||
"mocha": "--bail --check-leaks" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"eslint": "^3.12.2", | ||
"eslint-config-airbnb-base": "^11.0.0", | ||
"eslint": "^3.14.1", | ||
"eslint-config-nelson": "^0.2.0", | ||
"eslint-plugin-import": "^2.2.0", | ||
"graphql": "^0.8.2", | ||
"graphql-relay": "^0.4.4", | ||
"graphql": "^0.9.1", | ||
"graphql-relay": "^0.5.1", | ||
"istanbul": "^0.4.5", | ||
@@ -43,7 +42,7 @@ "mocha": "^3.2.0" | ||
"dependencies": { | ||
"n3": "^0.8.5" | ||
"n3": "^0.9.1" | ||
}, | ||
"peerDependencies": { | ||
"graphql": "^0.8.2" | ||
"graphql": "^0.9.1" | ||
} | ||
} |
# Semantic GraphQL | ||
[![npm version](https://badge.fury.io/js/semantic-graphql.svg)](https://www.npmjs.com/package/semantic-graphql) | ||
[![Build Status](https://travis-ci.org/nelson-ai/semantic-graphql.svg?branch=master)](https://travis-ci.org/nelson-ai/semantic-graphql) | ||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](#contributing) | ||
@@ -97,3 +98,5 @@ | ||
constructor(resolvers: Resolvers, config: ?SemanticGraphConfig), | ||
[subject: Iri]: object, | ||
nTriples: number, | ||
# The semantic data is stored directly on the graph | ||
[subject: Iri]: { [predicate: iri]: Array<object: string> }, | ||
# Public methods: | ||
@@ -128,3 +131,3 @@ addTriple: AddTripleFn, | ||
The names of the generated GraphQL objects will be prefixed. | ||
RDF, RDFS and OWL ontologies are by default prefixed with "Rdf", "Rdfs" and "Owl". | ||
RDF, RDFS and OWL ontologies are by default prefixed with "rdf", "rdfs" and "owl". | ||
So a fragment on rdfs:Resource should be "on RdfsResource". | ||
@@ -157,7 +160,7 @@ | ||
Will be removed someday | ||
Deprecated | ||
### parseFile | ||
Will be removed someday | ||
Deprecated | ||
@@ -204,3 +207,3 @@ ### getObjectType | ||
Adds a custom field "fieldName" on the GraphQLObjectType and GraphQLInterfaceType representing "classIri". | ||
Adds a custom field on the GraphQLObjectType and GraphQLInterfaceType representing a class. | ||
Throws if "classIri" is not found in the graph. | ||
@@ -225,2 +228,4 @@ Returns a random IRI referencing the new virtual rdf:Property. | ||
Resolvers are functions needed to resolve data. You have to code them. | ||
``` | ||
@@ -241,2 +246,4 @@ type Resolvers = { | ||
Given a source, resolve its id. | ||
``` | ||
@@ -251,6 +258,8 @@ type ResolveSourceIdFn = ( | ||
Must be sync if you use Relay. | ||
See [`globalIdField` source code](https://github.com/graphql/graphql-relay-js/blob/master/src/node/node.js#L107) | ||
See [`globalIdField`'s source code](https://github.com/graphql/graphql-relay-js/blob/master/src/node/node.js#L107) | ||
### resolveSourceTypes | ||
Given a source, resolve its rdf:type. Can be a single IRI since its super-classes will be infered. | ||
``` | ||
@@ -260,10 +269,9 @@ type ResolveSourceTypesFn = ( | ||
info?: GraphQLResolveInfo | ||
) => Iri | Array<Iri> | ||
) => ResolverOutput<class: Iri> | ||
``` | ||
Must be sync. | ||
See [this GraphQL issue](https://github.com/graphql/graphql-js/issues/398). | ||
### resolveSourcePropertyValue | ||
Given a source and the IRI of a predicate, resolve the objects for that source and predicate. | ||
``` | ||
@@ -280,2 +288,4 @@ type ResolveSourcePropertyValueFn = ( | ||
Given the IRI of a resource, resolve a "source" for other resolvers to use. You can either return the resource IRI to pass around as source, or fetch all the predicates/objects for that subject and return an object. | ||
``` | ||
@@ -291,2 +301,4 @@ type ResolveResourceFn = ( | ||
Same as `resolveResource` but with an array of IRIs. | ||
``` | ||
@@ -302,5 +314,7 @@ type ResolveResourcesFn = ( | ||
Given a array of possible rdf:type, a predicate and an object, resolve the subjects matching that pattern. | ||
``` | ||
type ResolveResourcesByPredicateFn = ( | ||
typeIri?: Array<Iri>, | ||
typeIris?: Array<Iri>, | ||
predicateIri?: Iri, | ||
@@ -346,3 +360,3 @@ value?: any, | ||
// Partial modifications to fields are achieved using | ||
_['http://foo.com#worksForCompany'].graphqlFieldConfigExtension = { | ||
_['http://foo.com#someOtherProperty'].graphqlFieldConfigExtension = { | ||
args: /* Look Ma', arguments! */ | ||
@@ -349,0 +363,0 @@ resolve: customResolveFn, |
const warn = require('../utils/warn'); | ||
// Possible bug: stack overflow | ||
// Both methods walk a graph: g | ||
@@ -4,0 +6,0 @@ // from a given vertex (:=startVertex): iri |
@@ -21,4 +21,5 @@ const { GraphQLList } = require('graphql'); | ||
// Look for a range, return it if found | ||
// Otherwise for each super-property, look for a range, if not found, check their super-properties and so on | ||
// In a breath-first manner | ||
// Otherwise for each super-property, look for a range, | ||
// if not found, check their super-properties and so on | ||
// TODO: check walklook, maybe test it | ||
const ranges = [...walklook(g, iri, rdfsSubPropertyOf, rdfsRange)]; | ||
@@ -25,0 +26,0 @@ const nRanges = ranges.length; |
@@ -14,5 +14,6 @@ const { GraphQLID } = require('graphql'); | ||
// Find superClasses of the class and their superClasses | ||
// Find super-classes of the class and their super-classes | ||
walkmap(g, iri, rdfsSubClassOf) | ||
// Many universal properties, like label and comment, have rdfs:Resource in their domain | ||
// Everything is a Resource, plus many universal properties | ||
// like label and comment, have rdfs:Resource in their domain | ||
.add(rdfsResource) | ||
@@ -19,0 +20,0 @@ // For each class, find properties on their domain |
@@ -8,3 +8,3 @@ const { rdfsSubClassOf, rdfsResource } = require('../constants'); | ||
// Find superClasses of the class and their superClasses | ||
// Find super-classes of the class and their super-classes | ||
walkmap(g, iri, rdfsSubClassOf) | ||
@@ -11,0 +11,0 @@ // Many universal properties, like label and comment, have rdfs:Resource in their domain |
const getIriLocalName = require('../utils/getIriLocalName'); | ||
const memorize = require('../graph/memorize'); | ||
const capitalize = require('../utils/capitalize'); | ||
// GraphQL-safe name | ||
// GraphQL-safe name for GraphQL types | ||
function getGraphqlName(g, iri) { | ||
@@ -12,5 +13,5 @@ const { prefixes } = g.config; | ||
return (prefix + localName).replace(/\W/g, '_'); | ||
return capitalize(prefix + localName).replace(/\W/g, '_'); | ||
} | ||
module.exports = memorize(getGraphqlName, 'graphqlName'); |
@@ -36,3 +36,3 @@ const { GraphQLScalarType } = require('graphql'); | ||
.map(({ parseLiteral }) => parseLiteral(ast)) | ||
.filter(value => value !== null)[0] || null, | ||
.filter(value => value !== null)[0] || null, // TODO: use find | ||
}); | ||
@@ -39,0 +39,0 @@ |
@@ -7,3 +7,2 @@ const { _rdfsSubClassOf, rdfsResource } = require('../constants'); | ||
// TODO: kill that bird | ||
function getGraphqlTypeResolver(g, iri) { | ||
@@ -15,6 +14,5 @@ | ||
return (source, info) => { | ||
const types = g.resolvers.resolveSourceTypes(source, info); | ||
// Everything is a resource, hence the fallback | ||
return (source, info) => Promise.resolve(g.resolvers.resolveSourceTypes(source, info)) | ||
.then(types => { | ||
// Everything is a rdfs:Resource, hence the fallback | ||
if (isNil(types)) return getGraphqlObjectType(g, rdfsResource); | ||
@@ -33,5 +31,5 @@ | ||
return getGraphqlObjectType(g, typesArray[0]); | ||
}; | ||
}); | ||
} | ||
module.exports = getGraphqlTypeResolver; |
const path = require('path'); | ||
const { readFileSync } = require('fs'); | ||
const createRdfParser = require('n3').Parser; | ||
const { rdfIri, rdfsIri, owlIri, rdfsDomain, rdfsResource } = require('./constants'); | ||
const { | ||
rdfIri, rdfsIri, owlIri, rdfsResource, | ||
rdfType, rdfsLabel, rdfsComment, rdfsDomain, rdfsRange, rdfsSubClassOf, rdfsSubPropertyOf, owlInverseOf, | ||
} = require('./constants'); | ||
const invariant = require('./utils/invariant'); | ||
@@ -17,9 +20,21 @@ const isIri = require('./utils/isIri'); | ||
const baseGraph = {}; | ||
const baseGraph = { nTriples: 0 }; | ||
const basePrefixes = { | ||
Rdf: rdfIri, | ||
Rdfs: rdfsIri, | ||
Owl: owlIri, | ||
rdf: rdfIri, | ||
rdfs: rdfsIri, | ||
owl: owlIri, | ||
}; | ||
// Only those predicates are useful for the lib, thus stored in the graph | ||
const workingPredicates = [ | ||
rdfType, | ||
rdfsLabel, | ||
rdfsComment, | ||
rdfsDomain, | ||
rdfsRange, | ||
rdfsSubClassOf, | ||
rdfsSubPropertyOf, | ||
owlInverseOf, | ||
]; | ||
parseFileAndIndex(baseGraph, '../ontologies/rdf.ttl'); | ||
@@ -56,3 +71,3 @@ parseFileAndIndex(baseGraph, '../ontologies/rdfs.ttl'); | ||
this.getConnectionType = iri => getRelayConnectionDefinitions(this, iri).connectionType; | ||
this.toString = () => '[SemanticGraph]'; | ||
this.toString = () => `[SemanticGraph: ${this.nTriples} triples]`; | ||
} | ||
@@ -91,2 +106,3 @@ | ||
function indexTriple(g, { subject, predicate, object }) { | ||
if (!workingPredicates.includes(predicate)) return; | ||
if (!(isIri(subject) && isIri(predicate)) || g[subject] && g[subject][predicate] && g[subject][predicate].includes(object)) return; | ||
@@ -96,2 +112,4 @@ | ||
g.nTriples++; | ||
if (isIri(object)) upsert(g, object, `_${predicate}`, subject); | ||
@@ -98,0 +116,0 @@ } |
@@ -6,6 +6,9 @@ /* global describe, it */ | ||
const commonTurtlePrefixes = require('./utils/commonTurtlePrefixes'); | ||
const SemanticGraph = require('..'); | ||
const castArrayShape = require('../src/utils/castArrayShape'); | ||
const isNil = require('../src/utils/isNil'); | ||
const capitalize = require('../src/utils/capitalize'); | ||
const { walkmap, walklook } = require('../src/graph/traversal'); | ||
const { rdfsClass, rdfType, _rdfsDomain } = require('../src/constants'); | ||
const ArrayKeyedMap = require('../src/ArrayKeyedMap'); | ||
const SemanticGraph = require('..'); | ||
@@ -15,3 +18,3 @@ // TODO: split this file | ||
// NOTE: getIriLocalName and isIri will be imported from an external lib someday | ||
describe('utils', () => { | ||
describe('Utils', () => { | ||
@@ -34,4 +37,93 @@ it('castArrayShape', () => { | ||
}); | ||
it('capitalize', () => { | ||
assert.strictEqual(capitalize(''), ''); | ||
assert.strictEqual(capitalize('abc'), 'Abc'); | ||
assert.strictEqual(capitalize('012'), '012'); | ||
}); | ||
}); | ||
describe('Graph traversal', () => { | ||
const graph = { | ||
a: { | ||
x: ['b', 'c'], | ||
y: ['d', 'e'], | ||
}, | ||
b: { | ||
x: ['a', 'c', 'd'], | ||
z: ['a', 'f'], | ||
}, | ||
c: { | ||
x: ['c'], | ||
}, | ||
d: { | ||
y: ['a', 'b'], | ||
z: ['c'], | ||
}, | ||
e: { | ||
z: ['c', 'd', 'g'], | ||
}, | ||
f: { | ||
x: ['g'], | ||
}, | ||
g: { | ||
y: ['a', 'f'], | ||
}, | ||
}; | ||
it('walkmap', () => { | ||
// Walmap should traverse the graph in a depth-first manner | ||
// using the same exit edge, until all vertices are visited | ||
assert.deepEqual([...walkmap(graph, 'a', 'x')], ['a', 'b', 'c', 'd']); | ||
assert.deepEqual([...walkmap(graph, 'b', 'x')], ['b', 'a', 'c', 'd']); | ||
assert.deepEqual([...walkmap(graph, 'a', 'y')], ['a', 'd', 'b', 'e']); | ||
assert.deepEqual([...walkmap(graph, 'b', 'y')], ['b']); | ||
assert.deepEqual([...walkmap(graph, 'b', 'z')], ['b', 'a', 'f']); | ||
}); | ||
it('walklook', () => { | ||
// Walklook should traverse the graph in a breath-first manner | ||
// Stopping once a fringe has resolved a result | ||
assert.deepEqual([...walklook(graph, 'a', 'x', 'y')], ['d', 'e']); | ||
assert.deepEqual([...walklook(graph, 'a', 'x', 'z')], ['a', 'f']); | ||
assert.deepEqual([...walklook(graph, 'g', 'y', 'x')], ['b', 'c', 'g']); // BFS with stop condition | ||
assert.deepEqual([...walklook(graph, 'g', 'x', 'z')], []); | ||
assert.deepEqual([...walklook(graph, 'c', 'x', 'z')], []); | ||
}); | ||
}); | ||
describe('ArrayKeyedMap', () => { | ||
it('get/set', () => { | ||
const akm = new ArrayKeyedMap(); | ||
assert.doesNotThrow(() => akm.set(['a', 'b', 'c'], 'foo')); | ||
assert.doesNotThrow(() => akm.get(['d'])); | ||
assert.strictEqual(akm.get(['a', 'b', 'c']), 'foo'); | ||
assert.strictEqual(akm.get(['a', 'c', 'b']), 'foo'); | ||
assert.strictEqual(akm.get(['b', 'c', 'a']), 'foo'); | ||
assert.strictEqual(akm.get(['b', 'a', 'c']), 'foo'); | ||
assert.strictEqual(akm.get(['c', 'a', 'b']), 'foo'); | ||
assert.strictEqual(akm.get(['c', 'b', 'a']), 'foo'); | ||
}); | ||
it('has', () => { | ||
const akm = new ArrayKeyedMap(); | ||
akm.set(['a', 'b', 'c'], 'foo'); | ||
assert.isTrue(akm.has(['a', 'b', 'c'])); | ||
assert.isTrue(akm.has(['a', 'c', 'b'])); | ||
assert.isTrue(akm.has(['b', 'c', 'a'])); | ||
assert.isTrue(akm.has(['b', 'a', 'c'])); | ||
assert.isTrue(akm.has(['c', 'a', 'b'])); | ||
assert.isTrue(akm.has(['c', 'b', 'a'])); | ||
assert.isFalse(akm.has([])); | ||
assert.isFalse(akm.has(['a'])); | ||
assert.isFalse(akm.has(['a', 'b'])); | ||
}); | ||
}); | ||
describe('SemanticGraph', () => { | ||
@@ -81,3 +173,3 @@ | ||
const subject = `${fooIri}Subject`; | ||
const predicate = `${fooIri}Predicate`; | ||
const predicate = rdfType; | ||
const _predicate = `_${predicate}`; | ||
@@ -84,0 +176,0 @@ const object = `${fooIri}Object`; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
412079
87
1436
406
+ Addedgraphql@0.9.6(transitive)
+ Addediterall@1.3.0(transitive)
+ Addedn3@0.9.1(transitive)
- Removedgraphql@0.8.2(transitive)
- Removediterall@1.0.2(transitive)
- Removedn3@0.8.5(transitive)
Updatedn3@^0.9.1