Skip to content

Commit

Permalink
feat: stream store
Browse files Browse the repository at this point in the history
  • Loading branch information
tpluscode committed Jan 9, 2025
1 parent c0535dc commit f4c8d7f
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 65 deletions.
5 changes: 5 additions & 0 deletions .changeset/neat-jeans-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"mocha-chai-rdf": patch
---

Added store functions to in-memory stream client
23 changes: 20 additions & 3 deletions lib/sparql-clients.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable camelcase */
import type * as Oxigraph from 'oxigraph'
import type { Quad, Term } from '@rdfjs/types'
import type { NamedNode, Quad, Quad_Graph, Term, DefaultGraph, Stream } from '@rdfjs/types'
import rdf from '@zazuko/env-node'
import toStream from 'into-stream'
import type { ParsingClient } from 'sparql-http-client/ParsingClient.js'
Expand Down Expand Up @@ -51,7 +52,7 @@ export function parsingClient(store: Oxigraph.Store): ParsingClient {
ask: ask.bind(null, store),
update: update.bind(null, store),
},
store: {} as unknown as ParsingClient['store'],
store: undefined,
}
}

Expand All @@ -69,6 +70,22 @@ export function streamClient(store: Oxigraph.Store): StreamClient {
ask: ask.bind(null, store),
update: update.bind(null, store),
},
store: {} as unknown as StreamClient['store'],
store: {
get(graph: Quad_Graph) {
return rdf.dataset(store.match(null, null, null, graph)).toStream()
},
async post(stream: Stream, { graph: to_graph_name }: { graph?: NamedNode | DefaultGraph } = {}) {
const data = await rdf.dataset().import(stream)
store.load(data.toCanonical(), { to_graph_name, format: 'nt' })
},
async put(stream: Stream, options?: { graph?: NamedNode | DefaultGraph }) {
if (options?.graph?.termType === 'NamedNode') {
store.update(`CLEAR SILENT GRAPH <${options?.graph?.value}>`)
} else {
store.update('CLEAR DEFAULT')
}
await this.post(stream, options)
},
},
}
}
13 changes: 6 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
],
"dependencies": {
"@rdfjs/to-ntriples": "^3.0.1",
"@types/sparql-http-client": "^3.0.5",
"@zazuko/env-node": "^2.1.3",
"into-stream": "^8.0.1",
"mocha-chai-jest-snapshot": "^1.1.6",
Expand All @@ -36,7 +37,6 @@
"@types/glob": "^8.1.0",
"@types/mocha": "^10.0.7",
"@types/rdfjs__to-ntriples": "^3.0.0",
"@types/sparql-http-client": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^7",
"@typescript-eslint/parser": "^7",
"c8": "^10.1.2",
Expand Down
190 changes: 136 additions & 54 deletions test/tests/sparql-clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const require = module.createRequire(import.meta.url)

use(matchers)

const ex = rdf.namespace('http://example.com/')

describe('sparql-clients.js', () => {
describe('parsingClient', () => {
let client: ParsingClient
Expand Down Expand Up @@ -90,7 +92,7 @@ describe('sparql-clients.js', () => {
})
})

describe('sparqlClient', () => {
describe('streamClient', () => {
let client: StreamClient
let store: Store

Expand All @@ -102,70 +104,150 @@ describe('sparql-clients.js', () => {
client = streamClient(store)
})

it('can be queried with SELECT', async () => {
const stream = client.query.select(`
SELECT ?name WHERE {
<http://localhost:8080/data/person/amy-farrah-fowler> <http://schema.org/givenName> ?name
}
`)
context('query', () => {
it('can be queried with SELECT', async () => {
const stream = client.query.select(`
SELECT ?name WHERE {
<http://localhost:8080/data/person/amy-farrah-fowler> <http://schema.org/givenName> ?name
}
`)

const results = await getStreamAsArray(stream)
const results = await getStreamAsArray(stream)

expect(results).to.deep.equal([
{ name: rdf.literal('Amy') },
])
})
expect(results).to.deep.equal([
{ name: rdf.literal('Amy') },
])
})

it('can be queried with CONSTRUCT', async () => {
const stream = client.query.construct(`
CONSTRUCT WHERE {
<http://localhost:8080/data/person/amy-farrah-fowler> <http://schema.org/givenName> ?name
}
`)
it('can be queried with CONSTRUCT', async () => {
const stream = client.query.construct(`
CONSTRUCT WHERE {
<http://localhost:8080/data/person/amy-farrah-fowler> <http://schema.org/givenName> ?name
}
`)

const results = await getStreamAsArray<Quad>(stream)
const results = await getStreamAsArray<Quad>(stream)

expect([...results][0]).to.equal(rdf.quad(
rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'),
rdf.ns.schema.givenName,
rdf.literal('Amy'),
))
})
expect([...results][0]).to.equal(rdf.quad(
rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'),
rdf.ns.schema.givenName,
rdf.literal('Amy'),
))
})

it('can be queried with ASK', async () => {
const result = await client.query.ask(`
ASK {
?person <http://schema.org/givenName> ?name
}
`)
it('can be queried with ASK', async () => {
const result = await client.query.ask(`
ASK {
?person <http://schema.org/givenName> ?name
}
`)

expect(result).to.equal(true)
})
expect(result).to.equal(true)
})

it('can be updated', async () => {
await client.query.update(`
INSERT {
GRAPH ?g {
?person <http://schema.org/name> ?newName
it('can be updated', async () => {
await client.query.update(`
INSERT {
GRAPH ?g {
?person <http://schema.org/name> ?newName
}
}
}
WHERE {
GRAPH ?g {
?person <http://schema.org/givenName> ?name ;
<http://schema.org/familyName> ?familyName .
BIND(CONCAT(?name, " ", ?familyName) AS ?newName)
WHERE {
GRAPH ?g {
?person <http://schema.org/givenName> ?name ;
<http://schema.org/familyName> ?familyName .
BIND(CONCAT(?name, " ", ?familyName) AS ?newName)
}
}
}
`)
`)

expect(store.match(rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'), rdf.ns.schema.name)).to.deep.equal([
rdf.quad(
rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'),
rdf.ns.schema.name,
rdf.literal('Amy Fowler'),
rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'),
),
])
})
})

expect(store.match(rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'), rdf.ns.schema.name)).to.deep.equal([
rdf.quad(
rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'),
rdf.ns.schema.name,
rdf.literal('Amy Fowler'),
rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'),
),
])
context('store', () => {
context('get', () => {
it('can fetch a select graph', async () => {
// when
const stream = client.store.get(rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler'))
const dataset = await rdf.dataset().import(stream)

// then
expect(dataset).to.have.property('size', 12)
})
})

context('post', () => {
it('can add to a named graph', async () => {
// given
const data = rdf.clownface()
.namedNode(ex.foo)
.addOut(rdf.ns.schema.name, 'Foo')

// when
await client.store.post(data.dataset.toStream(), {
graph: ex.Foo,
})

// then
expect(store.match(null, null, null, ex.Foo)).to.have.length(1)
})

it('can add to default graph', async () => {
// given
const data = rdf.clownface()
.namedNode(ex.foo)
.addOut(rdf.ns.schema.name, 'Foo')

// when
await client.store.post(data.dataset.toStream())

// then
expect(store.match(null, null, null, rdf.defaultGraph())).to.have.length(1)
})
})

context('put', () => {
it('can replace named graph', async () => {
// given
const graph = rdf.namedNode('http://localhost:8080/data/person/amy-farrah-fowler')
const data = rdf.clownface()
.namedNode(ex.foo)
.addOut(rdf.ns.schema.name, 'Foo')

// when
await client.store.put(data.dataset.toStream(), { graph })

// then
expect(store.match(null, null, null, graph)).to.have.length(1)
})

it('can replace default graph data', async () => {
// given
const foo = rdf.clownface()
.namedNode(ex.foo)
.addOut(rdf.ns.schema.name, 'Foo')
const bar = rdf.clownface()
.namedNode(ex.foo)
.addOut(rdf.ns.schema.name, 'Bar')

// when
await client.store.put(foo.dataset.toStream())
await client.store.put(bar.dataset.toStream())

// then
const matched = store.match(ex.foo, null, null, rdf.defaultGraph())
expect(matched).to.have.length(1)
expect(matched[0].object.value).to.have.eq('Bar')
})
})
})
})
})

0 comments on commit f4c8d7f

Please sign in to comment.