Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rdflib utils type index #123

Merged
merged 9 commits into from
Jul 25, 2024
18 changes: 13 additions & 5 deletions utils/rdflib/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

All notable changes to this module will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added

- [discoverType](https://solid-contrib.github.io/data-modules/rdflib-utils/classes/index.ModuleSupport.html#discoverType)

## 0.2.0

Expand All @@ -11,16 +19,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- [ModuleSupport](https://solid-contrib.github.io/data-modules/rdflib-utils/classes/index.ModuleSupport.html)
- [TypeIndexQuery](https://solid-contrib.github.io/data-modules/rdflib-utils/classes/index.TypeIndexQuery.html)
- [addInstanceToTypeIndex](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/index.addInstanceToTypeIndex.html)
- helper functions to generate terms in common Solid namespaces:
- helper functions to generate terms in common Solid namespaces:
- [ldp](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/index.ldp.html)
- [pim](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/index.pim.html)
- [rdf](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/index.rdf.html)
- [solid](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/index.solid.html)

### Breaking Change

- [generateId](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/identifier.generateId.html): Moved to submodule

- [generateId](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/identifier.generateId.html):
Moved to submodule

## 0.1.1

Expand All @@ -42,4 +50,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- [mockForbidden](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/test_support.mockForbidden.html)
- [mockLdpContainer](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/test_support.mockLdpContainer.html)
- [mockNotFound](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/test_support.mockNotFound.html)
- [mockTurtleDocument](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/test_support.mockTurtleDocument.html)
- [mockTurtleDocument](https://solid-contrib.github.io/data-modules/rdflib-utils/functions/test_support.mockTurtleDocument.html)
18 changes: 17 additions & 1 deletion utils/rdflib/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fetcher, IndexedFormula, UpdateManager } from "rdflib";
import { Fetcher, IndexedFormula, NamedNode, UpdateManager } from "rdflib";

export * from "./web-operations/index.js";
export * from "./queries/index.js";
Expand All @@ -11,3 +11,19 @@ export interface ModuleConfig {
fetcher: Fetcher;
updater: UpdateManager;
}

/**
* Lists instances and containers found in a type index
*/
export interface TypeRegistrations {
instanceContainers: NamedNode[];
instances: NamedNode[];
}

/**
* Type registrations grouped by whether they have been discovered in private or public type index.
*/
export interface TypeRegistrationsByVisibility {
public: TypeRegistrations;
private: TypeRegistrations;
}
132 changes: 132 additions & 0 deletions utils/rdflib/src/module/ModuleSupport.integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,138 @@ describe("ModuleSupport", () => {
).toBe(true);
});

describe("discoverType", () => {
describe("given registrations in public type index", () => {
it("returns all instances and containers from that index", async () => {
const { authenticatedFetch, support } = givenModuleSupport();

mockTurtleDocument(
authenticatedFetch,
"https://pod.test/alice/profile/card",
`
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.

<#me> a vcard:Individual;
vcard:fn "Alice";
solid:publicTypeIndex <https://pod.test/alice/settings/publicTypeIndex.ttl> ;
.
`,
);

mockTurtleDocument(
authenticatedFetch,
"https://pod.test/alice/settings/publicTypeIndex.ttl",
`
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
@prefix ex: <http://vocab.example#>.

<#registration-1> a solid:TypeRegistration ;
solid:forClass ex:Something ;
solid:instance <https://pod.test/alice/something/1/index.ttl#this>, <https://pod.test/alice/something/2/index.ttl#this> ;
.

<#registration-2> a solid:TypeRegistration ;
solid:forClass ex:Something ;
solid:instanceContainer <https://pod.test/alice/things/> ;
.
`,
);

const result = await support.discoverType(
sym("https://pod.test/alice/profile/card#me"),
sym("http://vocab.example#Something"),
);

expect(result.private).toEqual({
instances: [],
instanceContainers: [],
});

expect(result.public).toEqual({
instances: [
sym("https://pod.test/alice/something/1/index.ttl#this"),
sym("https://pod.test/alice/something/2/index.ttl#this"),
],
instanceContainers: [sym("https://pod.test/alice/things/")],
});
});
});

describe("given registrations in private type index", () => {
it("returns all instances and containers from that index", async () => {
const { authenticatedFetch, support } = givenModuleSupport();

mockTurtleDocument(
authenticatedFetch,
"https://pod.test/alice/profile/card",
`
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
@prefix pim: <http://www.w3.org/ns/pim/space#>.

<#me> a vcard:Individual;
vcard:fn "Alice";
pim:preferencesFile <https://pod.test/alice/settings/prefs.ttl> ;
.
`,
);

mockTurtleDocument(
authenticatedFetch,
"https://pod.test/alice/settings/prefs.ttl",
`
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.

<https://pod.test/alice/profile/card#me>
solid:privateTypeIndex <https://pod.test/alice/settings/privateTypeIndex.ttl> ;
.
`,
);

mockTurtleDocument(
authenticatedFetch,
"https://pod.test/alice/settings/privateTypeIndex.ttl",
`
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
@prefix ex: <http://vocab.example#>.

<#registration-1> a solid:TypeRegistration ;
solid:forClass ex:Something ;
solid:instance <https://pod.test/alice/something/1/index.ttl#this>, <https://pod.test/alice/something/2/index.ttl#this> ;
.

<#registration-2> a solid:TypeRegistration ;
solid:forClass ex:Something ;
solid:instanceContainer <https://pod.test/alice/things/> ;
.
`,
);

const result = await support.discoverType(
sym("https://pod.test/alice/profile/card#me"),
sym("http://vocab.example#Something"),
);

expect(result.private).toEqual({
instances: [
sym("https://pod.test/alice/something/1/index.ttl#this"),
sym("https://pod.test/alice/something/2/index.ttl#this"),
],
instanceContainers: [sym("https://pod.test/alice/things/")],
});

expect(result.public).toEqual({
instances: [],
instanceContainers: [],
});
});
});
});

describe("isContainer", () => {
it("returns true if url refers to a container", async () => {
const { authenticatedFetch, support } = givenModuleSupport();
Expand Down
71 changes: 69 additions & 2 deletions utils/rdflib/src/module/ModuleSupport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { Fetcher, IndexedFormula, Node, sym, UpdateManager } from "rdflib";
import { fetchNode, ModuleConfig } from "../index.js";
import { Fetcher, IndexedFormula, NamedNode, Node, sym, UpdateManager } from "rdflib";
import {
fetchNode,
ModuleConfig,
PreferencesQuery,
ProfileQuery,
TypeIndexQuery,
TypeRegistrationsByVisibility
} from "../index.js";
import { ldp, rdf } from "../namespaces/index.js";

/**
Expand Down Expand Up @@ -32,6 +39,66 @@ export class ModuleSupport {
return Promise.all(nodes.map((it) => this.fetchNode(it)));
}

/**
* Discover storage locations (instances or instance containers) for a given type by fetching and querying private and public type indexes
* @param webId - The WebID to search for type indexes
* @param typeNode - a NamedNode representing the type to discover
*/
async discoverType(
webId: NamedNode,
typeNode: NamedNode,
): Promise<TypeRegistrationsByVisibility> {
// 1. fetch webId
// 2.1 query profile for public type index
// 3.1 fetch public type index
// 4.1 query registrations
// 2.2 query profile for preferences file
// 3.2 fetch preferences file
// 4.2 query settings document for private type index
// 5. fetch private type index
// 6. query registrations

// 1.
await this.fetchNode(webId);
// 2.
const profileQuery = new ProfileQuery(webId, this.store);
const publicTypeIndex = profileQuery.queryPublicTypeIndex();
const preferencesFile = profileQuery.queryPreferencesFile();
// 3.
await Promise.allSettled([
this.fetchNode(publicTypeIndex),
this.fetchNode(preferencesFile),
]);
// 4.1
const noRegistrations = { instances: [], instanceContainers: [] };
const publicRegistrations = publicTypeIndex
? new TypeIndexQuery(
this.store,
publicTypeIndex,
).queryRegistrationsForType(typeNode)
: noRegistrations;
// 4.2
const privateTypeIndex = preferencesFile
? new PreferencesQuery(
this.store,
webId,
preferencesFile,
).queryPrivateTypeIndex()
: null;
// 5.
await this.fetchNode(privateTypeIndex);
const privateRegistrations = privateTypeIndex
? new TypeIndexQuery(
this.store,
privateTypeIndex,
).queryRegistrationsForType(typeNode)
: noRegistrations;
return {
private: privateRegistrations,
public: publicRegistrations,
};
}

/**
* Checks whether the resource identified by the given URL is a LDP container
* @param storageUrl - The URL to check
Expand Down
Loading
Loading