Updated the ldo docs

main
jaxoncreed 2 years ago
parent 0c0f037786
commit eabe009c44
  1. 3
      Readme.md
  2. 63679
      package-lock.json
  3. 55
      packages/cli/README.md
  4. 4
      packages/cli/src/index.ts
  5. 286
      packages/ldo/README.md
  6. 10
      packages/ldo/src/LdoDatasetFactory.ts
  7. 4
      packages/ldo/src/ShapeType.ts
  8. 9
      packages/ldo/src/createLdoDataset.ts
  9. 88
      packages/solid/test/ContainerRequester.test.ts
  10. 198
      packages/solid/test/SolidLdoDataset.test.ts
  11. 3
      packages/solid/test/utils.helper.ts
  12. 5
      packages/subscribable-dataset/src/WrapperSubscribableDataset.ts

@ -2,6 +2,9 @@
This is a monorepo that contains all libraries associated with Linked Data Objects (LDO). This is a monorepo that contains all libraries associated with Linked Data Objects (LDO).
## Documentation
Full documentation can be found at [ldo.js.org](https://ldo.js.org).
## Tutorial ## Tutorial
[A tutorial for how to use LDO](./documentation/solid-react-tutorial.md) is available here. [A tutorial for how to use LDO](./documentation/solid-react-tutorial.md) is available here.

63679
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,28 +1,61 @@
# LDO-CLI # @ldo/cli
A command line interface for Linked Data Objects. LDO-CLI builds `.shex` shapes into LDO types. The `@ldo/cli` is a command line interface for initializing LDO and building ShapeTypes.
## Setup ## Setup
Install the CLI
### Automatic Setup
To setup LDO, `cd` into your typescript project and run `npx @ldo/cli init`.
```bash ```bash
npm i @ldo/cli --save-dev cd my-typescript-project
npx @ldo/cli init
``` ```
Set up a shapes folder ### Manual Setup
The following is handled by the __automatic setup__:
Install the LDO dependencies.
```bash ```bash
mkdir .shapes npm install @ldo/ldo
npm install @ldo/cli --save-dev
``` ```
Place ShexC shapes inside `.shex` files Create a folder to store your ShEx shapes:
```bash ```bash
touch ./.shapes/example.shex mkdir shapes
```
Create a script to build ShEx shapes and convert them into Linked Data Objects. You can put this script in `package.json`
```json
{
...
scripts: {
...
"build:ldo": "ldo build --input ./shapes --output ./ldo"
...
}
...
}
``` ```
Build the shpaes ## Generating a ShapeType
@ldo/cli generates shape types using the `*.shex` files in the "input" folder. If you followed the instructions above, run the following command:
```bash ```bash
ldo build --input ./.shapes --output ./.ldo npm run build:ldo
```
This will generate five files:
- `./ldo/foafProfile.shapeTypes.ts` <-- This is the important file
- `./ldo/foafProfile.typings.ts`
- `./ldo/foafProfile.schema.ts`
- `./ldo/foafProfile.context.ts`
## API Details
- [`init` command](https://ldo.js.org/api/cli/init/)
- [`build` command](https://ldo.js.org/api/cli/build/)
``` ```
## Sponsorship ## Sponsorship

@ -12,8 +12,8 @@ program
program program
.command("build") .command("build")
.description("Build contents of a shex folder into Shape Types") .description("Build contents of a shex folder into Shape Types")
.option("-i, --input <inputPath>", "Provide the input path", "./shapes") .option("-i, --input <inputPath>", "Provide the input path", "./.shapes")
.option("-o, --output <outputPath>", "Provide the output path", "./ldo") .option("-o, --output <outputPath>", "Provide the output path", "./.ldo")
.action(build); .action(build);
program program

@ -1,89 +1,36 @@
# LDO (Linked Data Objects) # @ldo/ldo
LDO (Linked Data Objects) is a library that lets you easily manipulate RDF as if it were a standard TypeScript object that follows a [ShEx](https://shex.io) shape you define. `@ldo/ldo` is the primary interface for accessing Linked Data Objects given raw RDF.
For a full tutorial of using LDO to build React Solid applications, see [this tutorial](https://medium.com/@JacksonMorgan/building-solid-apps-with-ldo-6127a5a1979c). ## Guide
## Setup A full walkthrough for using the `@ldo/ldo` library can be found in the [For RDF Usage Guide](https://ldo.js.org/raw_rdf/).
### Automatic Setup ## Installation
To setup LDO, `cd` into your typescript project and run `npx @ldo/cli init`.
```bash ### Automatic Installation
cd my-typescript-project
npx @ldo/cli init
```
### Manual Setup
The following is handled by the __automatic setup__:
Install the LDO dependencies.
```bash
npm install @ldo/ldo
npm install @ldo/cli --save-dev
```
Create a folder to store your ShEx shapes: Navigate into your project's root folder and run the following command:
```bash
mkdir shapes
``` ```
cd my_project/
Create a script to build ShEx shapes and convert them into Linked Data Objects. You can put this script in `package.json` npx run @ldo/cli init
```json
{
...
scripts: {
...
"build:ldo": "ldo build --input ./shapes --output ./ldo"
...
}
...
}
``` ```
## Creating ShEx Schemas ### Manual Installation
LDO uses [ShEx](https://shex.io) as a schema for the RDF data in your project. To add a ShEx schema to your project, simply create a file ending in `.shex` to the `shapes` folder.
For more information on writing ShEx schemas see the [ShEx Primer](http://shex.io/shex-primer/index.html).
`./shapes/foafProfile.shex`: If you already have generated ShapeTypes, you may install the `@ldo/ldo` library independently.
```shex
PREFIX ex: <https://example.com/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
ex:FoafProfile EXTRA a {
a [ foaf:Person ]
// rdfs:comment "Defines the node as a Person (from foaf)" ;
foaf:name xsd:string ?
// rdfs:comment "Define a person's name." ;
foaf:img xsd:string ?
// rdfs:comment "Photo link but in string form" ;
foaf:knows @ex:FoafProfile *
// rdfs:comment "A list of WebIds for all the people this user knows." ;
}
``` ```
npm i @ldo/ldo
To build the shape, run:
```bash
npm run build:ldo
``` ```
This will generate five files:
- `./ldo/foafProfile.shapeTypes.ts` <-- This is the important file
- `./ldo/foafProfile.typings.ts`
- `./ldo/foafProfile.schema.ts`
- `./ldo/foafProfile.context.ts`
## Simple Example ## Simple Example
Below is a simple example of LDO in a real use-case (changing the name on a Solid Pod) Below is a simple example of LDO in a real use-case (changing the name on a Solid Pod). Assume that a ShapeType was previously generated and placed at `./.ldo/foafProfile.shapeTypes`.
```typescript ```typescript
import { parseRdf, startTransaction, toSparqlUpdate, toTurtle } from "ldo"; import { parseRdf, startTransaction, toSparqlUpdate, toTurtle } from "ldo";
import { FoafProfileShapeType } from "./ldo/foafProfile.shapeTypes"; import { FoafProfileShapeType } from "./.ldo/foafProfile.shapeTypes";
async function run() { async function run() {
const rawTurtle = ` const rawTurtle = `
@ -161,200 +108,49 @@ async function run() {
run(); run();
``` ```
## Getting an LDO Dataset ## API Details
An LDO Dataset is a kind of [RDF JS Dataset](https://rdf.js.org/dataset-spec/) that can create linked data objects.
LDO datasets can be created in two ways:
`createLdoDataset(initialDataset?: Dataset<Quad, Quad> | Quad[])`
```typescript
import { createLdoDataset } from "ldo";
const ldoDataset = createLdoDataset();
```
- `initialDataset`: An optional dataset or array of quads for the new dataset.
`parseRdf(data: string, parserOptions?: ParserOptions)`
```typescript
import { parseRdf } from "ldo";
const rawTurtle = "..."; Types
const ldoDataset = parseRdf(rawTurtle, { baseIRI: "https://example.com/" });
```
- `data`: The raw data to parse as a `string`. - [`LdoBase`](https://ldo.js.org/api/ldo/LdoBase/)
- `options` (optional): Parse options containing the following keys: - [`ShapeType`](https://ldo.js.org/api/ldo/ShapeType/)
- `format` (optional): The format the data is in. The following are acceptable formats: `Turtle`, `TriG`, `N-Triples`, `N-Quads`, `N3`, `Notation3`.
- `baseIRI` (optional): If this data is hosted at a specific location, you can provide the baseIRI of that location.
- `blankNodePrefix` (optional): If blank nodes should have a prefix, that should be provided here.
- `factory` (optional): a RDF Data Factory from [`@rdfjs/data-model`](https://www.npmjs.com/package/@rdfjs/data-model).
## Getting a Linked Data Object Getting a LdoDataset
Once you have an LdoDataset we can get a Linked Data Object. A linked data object feels just like a JavaScript object literal, but when you make modifications to it, it will affect the underlying LdoDataset.
Thie first step is defining which Shape Type you want to retrieve from the dataset. We can use the generated shape types and the `usingType()` method for this. - [`parseRdf`](https://ldo.js.org/api/ldo/parseRdf/)
- [`createLdoDatasetFactory`](https://ldo.js.org/api/ldo/createLdoDatasetFactory/)
- [`LdoDatasetFactory`](https://ldo.js.org/api/ldo/LdoDatasetFactory/)
- [`createLdoDataset`](https://ldo.js.org/api/ldo/createLdoDataset/)
- [`LdoDataset`](https://ldo.js.org/api/ldo/LdoDataset/)
```typescript Getting a Linked Data Object
import { FoafProfileShapeType } from "./ldo/foafProfile.shapeTypes.ts";
// ... Get the LdoDataset - [`LdoBuilder`](https://ldo.js.org/api/ldo/LdoBuilder/)
ldoDataset.usingType(FoafProfileShapeType); Converting a Linked Data Object to Raw RDF
```
Next, we want to identify exactly what part of the dataset we want to extract. We can do this in a few ways: - [`toTurtle`](https://ldo.js.org/api/ldo/toTurtle/)
- [`toNTriples`](https://ldo.js.org/api/ldo/toNTriples/)
- [`serialize`](https://ldo.js.org/api/ldo/serialize/)
### `.fromSubject(entryNode)` Transactions
`fromSubject` lets you define a an `entryNode`, the place of entry for the graph. The object returned by `jsonldDatasetProxy` will represent the given node. This parameter accepts both `namedNode`s and `blankNode`s. `fromSubject` takes a generic type representing the typescript type of the given subject.
```typescript - [transactions](https://ldo.js.org/api/ldo/transactions/)
const profile = ldoDataset - [`toSparqlUpdate`](https://ldo.js.org/api/ldo/toSparqlUpdate/)
.usingType(FoafProfileShapeType)
.fromSubject("http://example.com/Person1");
```
### `.matchSubject(predicate?, object?, graph?)` Language Tag Support
`matchSubject` returns a Jsonld Dataset Proxy representing all subjects in the dataset matching the given predicate, object, and graph.
```typescript - [`languageOf`](https://ldo.js.org/api/ldo/languageOf/)
const profiles = ldoDataset - [`setLanguagePreferences`](https://ldo.js.org/api/ldo/setLanguagePreferences/)
.usingType(FoafProfileShapeType)
.matchSubject(
namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
namedNode("http://xmlns.com/foaf/0.1/Person")
);
profiles.forEach((person) => {
console.log(person.fn);
});
```
### `.matchObject(subject?, predicate?, object?)` Graph Support
`matchObject` returns a Jsonld Dataset Proxy representing all objects in the dataset matching the given subject, predicate, and graph.
```typescript - [`graphOf`](https://ldo.js.org/api/ldo/graphOf/)
const friendsOfPerson1 = ldoDataset - [`write`](https://ldo.js.org/api/ldo/write/)
.usingType(FoafProfileShapeType)
.matchSubject(
namedNode("http://example.com/Person1"),
namedNode("http://xmlns.com/foaf/0.1/knows")
);
friendsOfPerson1.forEach((person) => {
console.log(person.fn);
});
```
### `.fromJson(inputData)` Other Helper Functions
`fromJson` will take any regular Json, add the information to the dataset, and return a Jsonld Dataset Proxy representing the given data.
```typescript - [`getDataset`](https://ldo.js.org/api/ldo/getDataset/)
const person2 = ldoDataset
.usingType(FoafProfileShapeType)
.fromJson({
"@id": "http://example.com/Person2",
fn: ["Jane Doe"],
});
```
## Getting and Setting Data on a Linked Data Object
Once you've created a Linked Data Object, you can get and set data as if it were a normal TypeScript Object. For specific details, see the documentation at [JSONLD Dataset Proxy](https://github.com/o-development/jsonld-dataset-proxy/blob/master/Readme.md).
```typescript
import { LinkedDataObject } from "ldo";
import { FoafProfileFactory } from "./ldo/foafProfile.ldoFactory.ts";
import { FoafProfile } from "./ldo/foafProfile.typings";
aysnc function start() {
const profile: FoafProfile = // Create LDO
// Logs "Aang"
console.log(profile.name);
// Logs "Person"
console.log(profile.type);
// Logs 1
console.log(profile.knows?.length);
// Logs "Katara"
console.log(profile.knows?.[0].name);
profile.name = "Bonzu Pippinpaddleopsicopolis III"
// Logs "Bonzu Pippinpaddleopsicopolis III"
console.log(profile.name);
profile.knows?.push({
type: "Person",
name: "Sokka"
});
// Logs 2
console.log(profile.knows?.length);
// Logs "Katara" and "Sokka"
profile.knows?.forEach((person) => console.log(person.name));
}
```
## Converting a Linked Data Object back to RDF
A linked data object can be converted into RDF in multiple ways:
### `toTurtle(linkedDataObject)`
```typescript
import { toTurtle } from "ldo"
// ...
const rawTurtle: string = await toTurtle(profile);
```
### `toNTiples(linkedDataObject)`
```typescript
import { toNTriples } from "ldo"
// ...
const rawNTriples: string = await toNTriples(profile);
```
### `serialize(linkedDataObject, options)`
```typescript
const rawTurtle: string = await profile.$serialize({
format: "Turtle",
prefixes: {
ex: "https://example.com/",
foaf: "http://xmlns.com/foaf/0.1/",
}
});
```
`serialize(linkedDataObject, options)` provides general serialization based on provided options:
- `foramt` (optional): The format to serialize to. The following are acceptable formats: `Turtle`, `TriG`, `N-Triples`, `N-Quads`, `N3`, `Notation3`.
- `prefixes`: The prefixes for those serializations that use prefixes.
## Transactions
Sometimes, you want to keep track of changes you make for the object. This is where transactions come in handy.
To start a transaction, use the `startTransaction(linkedDataObject)` function. From then on, all transactions will be tracked, but not added to the original ldoDataset. You can view the changes using the `transactionChanges(linkedDataObject)` or `toSparqlUpdate(linkedDataObject)` methods. When you're done with the transaction, you can run the `commitTransaction(linkedDataObject)` method to add the changes to the original ldoDataset.
```typescript
import {
startTransaction,
transactionChanges,
toSparqlUpdate,
commitTransaction,
} from "ldo";
// ... Get the profile linked data object
startTransaction(profile);
profile.name = "Kuzon"
const changes = transactionChanges(profile));
// Logs: <https://example.com/aang> <http://xmlns.com/foaf/0.1/name> "Kuzon"
console.log(changes.added?.toString())
// Logs: <https://example.com/aang> <http://xmlns.com/foaf/0.1/name> "Aang"
console.log(changes.removed?.toString())
console.log(await toSparqlUpdate(profile));
commitTransaction(profile);
```
## Other LDO Helper Functions
### `getDataset(linkedDataObject)`
Returns the Linked Data Object's underlying RDFJS dataset. Modifying this dataset will change the Linked Data Object as well.
```typescript
import { getDataset } from "ldo"
const dataset = getDataset(profile);
```
## Sponsorship ## Sponsorship
This project was made possible by a grant from NGI Zero Entrust via nlnet. Learn more on the [NLnet project page](https://nlnet.nl/project/SolidUsableApps/). This project was made possible by a grant from NGI Zero Entrust via nlnet. Learn more on the [NLnet project page](https://nlnet.nl/project/SolidUsableApps/).

@ -6,10 +6,20 @@ import { LdoDataset } from "./LdoDataset";
*/ */
export class LdoDatasetFactory implements DatasetFactory<Quad, Quad> { export class LdoDatasetFactory implements DatasetFactory<Quad, Quad> {
private datasetFactory: DatasetFactory<Quad, Quad>; private datasetFactory: DatasetFactory<Quad, Quad>;
/**
* @constructor
* @param datasetFactory A generic dataset factory this factory will wrap
*/
constructor(datasetFactory: DatasetFactory<Quad, Quad>) { constructor(datasetFactory: DatasetFactory<Quad, Quad>) {
this.datasetFactory = datasetFactory; this.datasetFactory = datasetFactory;
} }
/**
* Creates an LdoDataset
* @param quads A list of quads to initialize the dataset
* @returns an LdoDataset
*/
dataset(quads?: Dataset<Quad, Quad> | Quad[]): LdoDataset { dataset(quads?: Dataset<Quad, Quad> | Quad[]): LdoDataset {
return new LdoDataset( return new LdoDataset(
this.datasetFactory, this.datasetFactory,

@ -2,7 +2,9 @@ import type { ContextDefinition } from "jsonld";
import type { Schema } from "shexj"; import type { Schema } from "shexj";
import type { LdoBase } from "./util"; import type { LdoBase } from "./util";
// eslint-disable-next-line @typescript-eslint/no-explicit-any /**
* A collection of information required by LDO
*/
export type ShapeType<Type extends LdoBase> = { export type ShapeType<Type extends LdoBase> = {
schema: Schema; schema: Schema;
shape: string; shape: string;

@ -2,6 +2,10 @@ import type { Dataset, DatasetFactory, Quad } from "@rdfjs/types";
import { createDataset } from "@ldo/dataset"; import { createDataset } from "@ldo/dataset";
import { LdoDatasetFactory } from "./LdoDatasetFactory"; import { LdoDatasetFactory } from "./LdoDatasetFactory";
/**
* Creates an LDO Dataset Factory
* @returns An LDO Dataset Factory
*/
export function createLdoDatasetFactory() { export function createLdoDatasetFactory() {
const datasetFactory: DatasetFactory<Quad> = { const datasetFactory: DatasetFactory<Quad> = {
dataset: (quads?: Dataset<Quad> | Quad[]): Dataset<Quad> => { dataset: (quads?: Dataset<Quad> | Quad[]): Dataset<Quad> => {
@ -11,6 +15,11 @@ export function createLdoDatasetFactory() {
return new LdoDatasetFactory(datasetFactory); return new LdoDatasetFactory(datasetFactory);
} }
/**
* Create an LDO Dataset
* @param initialDataset
* @returns An LDO Dataset Factory
*/
export function createLdoDataset( export function createLdoDataset(
initialDataset?: Dataset<Quad, Quad> | Quad[], initialDataset?: Dataset<Quad, Quad> | Quad[],
) { ) {

@ -1,88 +0,0 @@
// import type { App } from "@solid/community-server";
// import { getAuthenticatedFetch, ROOT_CONTAINER } from "./solidServer.helper";
// import type { SolidLdoDataset } from "../src/SolidLdoDataset";
// import { createSolidLdoDataset } from "../src/createSolidLdoDataset";
// import { ContainerRequester } from "../src/requester/ContainerRequester";
// import type { ContainerUri } from "../src/util/uriTypes";
// describe.skip("Container Requester", () => {
// let _app: App;
// let authFetch: typeof fetch;
// let fetchMock: typeof fetch;
// let solidLdoDataset: SolidLdoDataset;
// beforeAll(async () => {
// // Start up the server
// // app = await createApp();
// // await app.start();
// authFetch = await getAuthenticatedFetch();
// });
// beforeEach(async () => {
// fetchMock = jest.fn(authFetch);
// solidLdoDataset = createSolidLdoDataset({ fetch: fetchMock });
// // Create a new document called sample.ttl
// await Promise.all([
// authFetch(`${ROOT_CONTAINER}test_leaf/`, {
// method: "POST",
// headers: { "content-type": "text/turtle", slug: "sample.ttl" },
// body: `@base <http://example.org/> .
// @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
// @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
// @prefix foaf: <http://xmlns.com/foaf/0.1/> .
// @prefix rel: <http://www.perceive.net/schemas/relationship/> .
// <#green-goblin>
// rel:enemyOf <#spiderman> ;
// a foaf:Person ; # in the context of the Marvel universe
// foaf:name "Green Goblin" .
// <#spiderman>
// rel:enemyOf <#green-goblin> ;
// a foaf:Person ;
// foaf:name "Spiderman", "Человек-паук"@ru .`,
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/`, {
// method: "PUT",
// headers: { "content-type": "text/plain", slug: "sample.txt" },
// body: `some text.`,
// }),
// ]);
// });
// afterEach(async () => {
// await Promise.all([
// authFetch(`${ROOT_COONTAINER}test_leaf/sample.ttl`, {
// method: "DELETE",
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/sample2.ttl`, {
// method: "DELETE",
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/sample.txt`, {
// method: "DELETE",
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/sample2.txt`, {
// method: "DELETE",
// }),
// ]);
// });
// it("Checks if a root container is a root container", async () => {
// const leafRequester = new ContainerRequester(
// `${ROOT_COONTAINER}` as ContainerUri,
// solidLdoDataset.context,
// );
// const result = await leafRequester.isRootContainer();
// expect(result).toBe(true);
// });
// it("Checks if a non root container is a root container", async () => {
// const leafRequester = new ContainerRequester(
// `${ROOT_COONTAINER}/test_leaf/`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.isRootContainer();
// expect(result).toBe(false);
// });
// });

@ -6,7 +6,8 @@ import {
createApp, createApp,
getAuthenticatedFetch, getAuthenticatedFetch,
} from "./solidServer.helper"; } from "./solidServer.helper";
import { namedNode } from "@rdfjs/data-model"; import { namedNode, quad as createQuad } from "@rdfjs/data-model";
import { wait } from "./utils.helper";
const TEST_CONTAINER_SLUG = "test_ldo/"; const TEST_CONTAINER_SLUG = "test_ldo/";
const TEST_CONTAINER_URI = const TEST_CONTAINER_URI =
@ -30,6 +31,24 @@ const SPIDER_MAN_TTL = `@base <http://example.org/> .
rel:enemyOf <#green-goblin> ; rel:enemyOf <#green-goblin> ;
a foaf:Person ; a foaf:Person ;
foaf:name "Spiderman", "Человек-паук"@ru .`; foaf:name "Spiderman", "Человек-паук"@ru .`;
const TEST_CONTAINER_TTL = `@prefix dc: <http://purl.org/dc/terms/>.
@prefix ldp: <http://www.w3.org/ns/ldp#>.
@prefix posix: <http://www.w3.org/ns/posix/stat#>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
<> <urn:npm:solid:community-server:http:slug> "sample.txt";
a ldp:Container, ldp:BasicContainer, ldp:Resource;
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime.
<sample.ttl> a ldp:Resource, <http://www.w3.org/ns/iana/media-types/text/turtle#Resource>;
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime.
<sample.txt> a ldp:Resource, <http://www.w3.org/ns/iana/media-types/text/plain#Resource>;
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime.
<> posix:mtime 1697810234;
ldp:contains <sample.ttl>, <sample.txt>.
<sample.ttl> posix:mtime 1697810234;
posix:size 522.
<sample.txt> posix:mtime 1697810234;
posix:size 10.`;
describe("SolidLdoDataset", () => { describe("SolidLdoDataset", () => {
let app: App; let app: App;
@ -95,6 +114,9 @@ describe("SolidLdoDataset", () => {
]); ]);
}); });
/**
* Read
*/
describe("read", () => { describe("read", () => {
it("Reads a data leaf", async () => { it("Reads a data leaf", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
@ -199,7 +221,7 @@ describe("SolidLdoDataset", () => {
it("Returns an error if there is no link header for a container request", async () => { it("Returns an error if there is no link header for a container request", async () => {
fetchMock.mockResolvedValueOnce( fetchMock.mockResolvedValueOnce(
new Response(SPIDER_MAN_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 200, status: 200,
headers: new Headers({ "content-type": "text/turtle" }), headers: new Headers({ "content-type": "text/turtle" }),
}), }),
@ -214,4 +236,176 @@ describe("SolidLdoDataset", () => {
); );
}); });
}); });
/**
* Get Root Container
*/
describe("rootContainer", () => {
it("Finds the root container", async () => {
const resource = solidLdoDataset.getResource(SAMPLE2_BINARY_URI);
const result = await resource.getRootContainer();
expect(result.type).toBe("container");
if (result.type !== "container") return;
expect(result.uri).toBe(ROOT_CONTAINER);
});
it("Returns an error if there is no link header for a container request", async () => {
fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, {
status: 200,
headers: new Headers({ "content-type": "text/turtle" }),
}),
);
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
const result = await resource.getRootContainer();
expect(result.isError).toBe(true);
if (!result.isError) return;
expect(result.type).toBe("noncompliantPodError");
expect(result.message).toBe(
"Response from https://solidweb.me/jackson3/test_ldo/ is not compliant with the Solid Specification: No link header present in request.",
);
});
it("An error to be returned if a common http error is encountered", async () => {
fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, {
status: 500,
}),
);
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
const result = await resource.getRootContainer();
expect(result.isError).toBe(true);
expect(result.type).toBe("serverError");
});
it("Returns an UnexpectedResourceError if an unknown error is triggered", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
const result = await resource.getRootContainer();
expect(result.isError).toBe(true);
if (!result.isError) return;
expect(result.type).toBe("unexpectedResourceError");
expect(result.message).toBe("Something happened.");
});
});
/**
* Create Data Resource
*/
describe("createDataResource", () => {
it("creates a document that doesn't exist", async () => {
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const container = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockImplementationOnce(async (...args) => {
console.log("before");
await wait(500);
console.log("after");
return authFetch(...args);
});
const [result] = await Promise.all([
resource.createAndOverwrite(),
(async () => {
await wait(100);
console.log("Checking");
expect(resource.isLoading()).toBe(true);
expect(resource.isCreating()).toBe(true);
expect(resource.isReading()).toBe(false);
expect(resource.isUploading).toBe(false);
expect(resource.isReloading()).toBe(false);
expect(resource.isDeleting()).toBe(false);
})(),
]);
expect(result.type).toBe("createSuccess");
expect(
solidLdoDataset.has(
createQuad(
namedNode(TEST_CONTAINER_URI),
namedNode("http://www.w3.org/ns/ldp#contains"),
namedNode(SAMPLE2_DATA_URI),
namedNode(TEST_CONTAINER_URI),
),
),
).toBe(true);
expect(
container.children().some((child) => child.uri === SAMPLE2_DATA_URI),
).toBe(true);
});
// it("creates a data resource that doesn't exist while overwriting", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample2.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.createDataResource(true);
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample2.ttl`),
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// ),
// ),
// ).toBe(true);
// });
// it("creates a data resource that does exist while not overwriting", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.createDataResource();
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode("http://example.org/#spiderman"),
// namedNode("http://www.perceive.net/schemas/relationship/enemyOf"),
// namedNode("http://example.org/#green-goblin"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// ),
// ),
// ).toBe(true);
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// ),
// ),
// ).toBe(true);
// });
// it("creates a data resource that does exist while overwriting", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.createDataResource(true);
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode("http://example.org/#spiderman"),
// namedNode("http://www.perceive.net/schemas/relationship/enemyOf"),
// namedNode("http://example.org/#green-goblin"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// ),
// ),
// ).toBe(false);
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// ),
// ),
// ).toBe(true);
// });
});
}); });

@ -0,0 +1,3 @@
export async function wait(millis: number) {
return new Promise((resolve) => setTimeout(resolve, millis));
}

@ -49,9 +49,8 @@ export default class WrapperSubscribableDataset<
new Map(); new Map();
/** /**
* * @param datasetFactory A RDF/JS Dataset Factory
* @param datasetFactory * @param initialDataset An RDF/JS Dataset with initial Quads
* @param initialDataset
*/ */
constructor( constructor(
datasetFactory: DatasetFactory<InAndOutQuad, InAndOutQuad>, datasetFactory: DatasetFactory<InAndOutQuad, InAndOutQuad>,

Loading…
Cancel
Save