Skip to content

Commit

Permalink
[refactor] Jest globals in tests, Readme examples, MST types
Browse files Browse the repository at this point in the history
  • Loading branch information
amivanoff committed Aug 20, 2024
1 parent 628cc67 commit 7344261
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 113 deletions.
92 changes: 72 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,105 @@
It is an intelligent SPARQL Client with SHACL Shapes support. You could use JSON objects and JSON Schemas to query SPARQL Endpoint, semi-similar to GraphQL style.

Some core features requires:

- RDF4J REST API in addition to standard SPARQL 1.1 Query and SPARQL 1.1 Update.
- SHACL Shapes for rdfs classes in server RDF repository.

## Features

- Retrieves lazily SPARQL Prefixes (namespaces) from server (requires RDF4J REST API)
- Retrieves lazily SHACL Shapes from server, converts it into JSON Schems and UI Schems and caches them
- Retrieves lazily SHACL Shapes from server, converts it into JSON Schemas and UI Schemas and caches them
- Uses JS objects and JSON Schemas (converted from shapes) to generate SPARQL Select Queries and SPARQL Update queries
- Converts SPARQL Results into JS objects for consumption in apps
- Have API server RDF repository creation and deletion (requires RDF4J REST API)
- Have API server RDF repository creation and deletion (requires RDF4J REST API)
- Supports bulk-load data from local files to server RDF repository (requires RDF4J REST API)

## Usage

### RDF4J Repository Creation and Data Uploading (low level, without MST)

```typescript
const provider = new ObjectProviderImpl();
provider.setUser('users:guest');
const client = provider.getClient();
client.setServerUrl('<url to ref4j rest api server>');
await client.createRepositoryAndSetCurrent( {'Repository ID': 'myrepo'} );
await uploadFiles(client, files, rootFolder);
import { SparqlClientImpl, uploadFiles } from '@agentlab/sparql-jsld-client';

//select all objects by schema (by rdfs class)
const artifacts = await provider.selectObjects('rm:Artifact');
const client = new SparqlClientImpl('http://localhost:8181/rdf4j-server');
await client.createRepositoryAndSetCurrent({ "Repository ID": "reqs2" }, 'native-rdfs-dt);

const files = [
{
file: 'vocabs/rm.ttl',
baseURI: '<http://cpgu.kbpm.ru/ns/rm/rdf#>',
},
{
file: 'shapes/shacl/rm/rm-shapes.ttl',
baseURI: '<http://cpgu.kbpm.ru/ns/rm/rdf#>',
},
{
file: 'data/cpgu/sample-collection.ttl',
baseURI: '<http://cpgu.kbpm.ru/ns/rm/reqs#>',
},
];
await uploadFiles(client, files, './test/data/');
```
### RDF4J Client SPARQL Queries (low level, without MST)
```typescript
const results = await client.sparqlSelect('SELECT * WHERE ?s ?p ?o');
```
### Reactive Collections (with MST and SPARQL Queries Generation)
//select all objects by schema (by rdfs class) with explisit schema object
const artifactSchema = await provider.getSchemaByUri('rm:Artifact');
const artifacts2 = await provider.selectObjects(artifactSchema);
```typescript
import { MstRepository, rootModelInitialState } from '@agentlab/sparql-jsld-client';

//select all objects by schema and conditions
const artifact30000 = await provider.selectObjectsWithTypeInfo(artifactSchema, { identifier: 30000 });
const artifactsFromFolder = await provider.selectObjects(artifactSchema, { assetFolder: 'folders:samples_module' });
const repository = MstRepository.create(rootModelInitialState, { client }); // client -- the same SparqlClientImpl instance
repository.setId('reqs2');

//select all objects by schema (by rdfs class)
const coll = repository.addColl('rm:ArtifactShape');
await coll.loadColl();
const dataJs: JsObject[] = coll.dataJs;
console.log(dataJs);

//select all objects by schema and conditions and process it reactively
const coll2 = repository.addColl({
entConstrs: [
{
schema: 'rm:ArtifactShape',
conditions: {
identifier: 30000,
},
},
],
});
when(
() => coll2 !== undefined && coll2.data.length > 0,
() => {
const dataJs2: JsObject[] = coll2.dataJs;
console.log(dataJs2);
repository.removeColl(coll2); // cleanup to save some memory
},
);
```
## Local Development
This project was bootstrapped with [TSDX](https://github.com/jaredpalmer/tsdx).
Use pnpm.
To to override the default settings from .env.test create file .env.test.local (git-ignored).
For single test run
```bash
yarn test -- -t "should select namespaces"
pnpm test -- -t "should select namespaces"
```
For single testsuite run
```bash
yarn test SparqlClient.spec.ts
pnpm test SparqlClient.spec.ts
```
## License
- [Eclipse Public License 2.0](LICENSE)
- [GPL 3.0](LICENSE)
8 changes: 4 additions & 4 deletions src/models/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ import { MstRepository } from './MstRepository';
export const rootModelInitialState: any = {
repId: '',
user: {
//'@id': 'mailto:[email protected]',//<mailto:[email protected]>
login: '[email protected]',
name: 'Guest',
},
processArea: 'projects:gishbbProject',
ns: {
current: {
rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
xsd: 'http://www.w3.org/2001/XMLSchema#',
sesame: 'http://www.openrdf.org/schema/sesame#',
aldkg: 'https://agentlab.ru/ldkg/onto#',
sesame: 'http://www.openrdf.org/schema/sesame#', //rdf4j DirectType Inferencer sesame:directSubClassOf
rdf4j: 'http://rdf4j.org/schema/rdf4j#', //rdf4j Default Graph rdf4j:nil, rdf4j:SHACLShapeGraph
aldkg: 'https://agentlab.eu/ldkg/onto#',
},
},
schemas: {
Expand Down
19 changes: 10 additions & 9 deletions src/models/MstColl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dayjs from 'dayjs';
import { isEqual } from 'lodash-es';
import { reaction } from 'mobx';
import {
applySnapshot,
types,
Instance,
flow,
Expand Down Expand Up @@ -144,7 +145,7 @@ export const MstColl = types
/**
* Views
*/
.views((self: any) => {
.views((self) => {
return {
/**
* Returns collection objects
Expand All @@ -163,15 +164,15 @@ export const MstColl = types
return self.dataIntrnl;
},
get dataJs() {
return getSnapshot(this.data);
return getSnapshot<JsObject[]>(this.data);
},
dataByIri(iri: string) {
return self.dataIntrnl.find((e: any) => {
return self.dataIntrnl.find((e) => {
// should take care of different element type API differences
const eType = getType(e);
if (isMapType(eType)) return e.get('@id') === iri; // [] returns undef in MST Map
if (isModelType(eType)) return e['@id'] === iri; // .get() returns exception in MST Model
const eSnp: any = getSnapshot(e); // MST Frozen???
if (isModelType(eType)) return (e as any)['@id'] === iri; // .get() returns exception in MST Model
const eSnp = getSnapshot<JsObject>(e); // MST Frozen???
return eSnp['@id'] === iri;
});
},
Expand All @@ -180,7 +181,7 @@ export const MstColl = types
/**
* Actions
*/
.actions((self: any) => {
.actions((self) => {
const rep: IAnyStateTreeNode = getRoot(self);
const client = getEnv(self).client;
let dispose: any;
Expand All @@ -197,7 +198,7 @@ export const MstColl = types
dispose = reaction(
() => {
const collConstr = self.collConstr;
const collConstrJs = getSnapshot(collConstr);
const collConstrJs = collConstr ? getSnapshot(collConstr) : undefined;
//console.log('Coll reaction', collConstrJs);
return collConstrJs;
},
Expand Down Expand Up @@ -256,8 +257,8 @@ export const MstColl = types
rep.ns.currentJs,
client,
);
if (objects === null) self.dataIntrnl = [];
else self.dataIntrnl = objects;
if (objects === null) applySnapshot(self.dataIntrnl, []);
else applySnapshot(self.dataIntrnl, objects);
//schema: {},
//selectQuery: '',
self.lastSynced = dayjs().valueOf();
Expand Down
8 changes: 4 additions & 4 deletions src/models/MstCollConstr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const MstEntConstr = types
*/
resolveType: types.maybe(types.boolean),
})
.views((self: any) => {
.views((self) => {
return {
get schemaJs() {
return self.schema ? getSnapshot(self.schema) : undefined;
Expand All @@ -104,7 +104,7 @@ export const MstEntConstr = types
},
};
})
.actions((self: any) => {
.actions((self) => {
//let disp: any;
return {
/*afterAttach() {
Expand Down Expand Up @@ -166,7 +166,7 @@ export const MstCollConstr = types
/**
* Views
*/
.views((self: any) => {
.views((self) => {
const rep: IAnyStateTreeNode = getRoot(self);
const client = getEnv(self).client;
return {
Expand All @@ -191,7 +191,7 @@ export const MstCollConstr = types
/**
* Actions
*/
.actions((self: any) => {
.actions((self) => {
let disp: any;
return {
/*afterAttach() {
Expand Down
6 changes: 3 additions & 3 deletions src/models/MstRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ export const MstRepository = types
if (!coll) return undefined;
const id = self.selectedData.get(iri);
if (id) {
let data = coll.dataByIri(id);
if (data) data = getSnapshot(data);
return data;
const data = coll.dataByIri(id);
if (data) return getSnapshot<JsObject>(data);
}
return undefined;
},
};
})
Expand Down
9 changes: 5 additions & 4 deletions test/Artifact.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* SPDX-License-Identifier: GPL-3.0-only
********************************************************************************/
import { afterAll, beforeAll, describe, expect, jest, it } from '@jest/globals';
import assert from 'assert';
import { getSnapshot } from 'mobx-state-tree';

import { rootModelInitialState } from '../src/models/Model';
Expand All @@ -20,7 +21,7 @@ import { rdfServerUrl, rmRepositoryParam, rmRepositoryType } from './config';
import { vocabsFiles, shapesFiles, usersFiles, projectsFoldersFiles, samplesFiles, rootFolder } from './configTests';
import { textFormatUri } from './schema/TestSchemas';

import { genTimestampedName, sleep } from './TestHelpers';
import { expectToBeDefined, genTimestampedName, sleep } from './TestHelpers';

// See https://stackoverflow.com/questions/49603939/async-callback-was-not-invoked-within-the-5000ms-timeout-specified-by-jest-setti
jest.setTimeout(500000);
Expand Down Expand Up @@ -54,8 +55,8 @@ beforeAll(async () => {
afterAll(async () => {
try {
await client.deleteRepository(rmRepositoryID);
} catch (err) {
fail(err);
} catch (err: any) {
assert.fail(err);
}
});

Expand Down Expand Up @@ -83,7 +84,7 @@ describe('create-artifact-scenario', () => {

const coll = repository.addColl('rm:ArtifactShape');
await coll.loadColl();
expect(coll).not.toBeUndefined();
expectToBeDefined(coll);
const data: any[] = coll && coll.data !== undefined ? getSnapshot(coll.data) : [];
expect(data.length).toBe(15);

Expand Down
Loading

0 comments on commit 7344261

Please sign in to comment.