Untested implementation of init type index

main
Jackson Morgan 8 months ago
parent 16376a4e7f
commit bc9f10db0b
  1. 16
      package-lock.json
  2. 3
      packages/solid-type-index/package.json
  3. 3
      packages/solid-type-index/src/.ldo/typeIndex.context.ts
  4. 1
      packages/solid-type-index/src/constants.ts
  5. 39
      packages/solid-type-index/src/getTypeIndex.ts
  6. 4
      packages/solid-type-index/src/index.ts
  7. 0
      packages/solid-type-index/src/react/useAddToTypeIndex.ts
  8. 0
      packages/solid-type-index/src/react/useRemoveFromTypeIndex.ts
  9. 200
      packages/solid-type-index/src/setTypeIndex.ts
  10. 13
      packages/solid-type-index/src/util/Options.ts
  11. 24
      packages/solid-type-index/test/General.test.tsx
  12. 8
      packages/solid-type-index/test/React.tsx
  13. 110
      packages/solid-type-index/test/setUpServer.ts
  14. 3
      packages/solid/src/SolidLdoTransactionDataset.ts

16
package-lock.json generated

@ -29738,7 +29738,8 @@
"license": "MIT",
"dependencies": {
"@ldo/solid": "^0.0.1-alpha.28",
"@ldo/solid-react": "^0.0.1-alpha.28"
"@ldo/solid-react": "^0.0.1-alpha.28",
"uuid": "^11.0.5"
},
"devDependencies": {
"@ldo/rdf-utils": "^0.0.1-alpha.24",
@ -29810,6 +29811,19 @@
"node": ">=4.2.0"
}
},
"packages/solid-type-index/node_modules/uuid": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz",
"integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/esm/bin/uuid"
}
},
"packages/solid/node_modules/ts-jest": {
"version": "27.1.5",
"dev": true,

@ -37,7 +37,8 @@
},
"dependencies": {
"@ldo/solid": "^0.0.1-alpha.28",
"@ldo/solid-react": "^0.0.1-alpha.28"
"@ldo/solid-react": "^0.0.1-alpha.28",
"uuid": "^11.0.5"
},
"files": [
"dist",

@ -6,6 +6,9 @@ import { LdoJsonldContext } from "@ldo/jsonld-dataset-proxy";
* =============================================================================
*/
export const typeIndexContext: LdoJsonldContext = {
type: {
"@id": "@type"
},
TypeIndex: {
"@id": "http://www.w3.org/ns/solid/terms#TypeIndex",
"@context": {

@ -1,3 +1,4 @@
export const RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
export const TYPE_REGISTRATION =
"http://www.w3.org/ns/solid/terms#TypeRegistration";
export const FOR_CLASS = "http://www.w3.org/2006/vcard/ns#forClass";

@ -1,31 +1,20 @@
import type { ContainerUri, LeafUri, SolidLdoDataset } from "@ldo/solid";
import { createSolidLdoDataset } from "@ldo/solid";
import type { ContainerUri, LeafUri } from "@ldo/solid";
import type { TypeRegistration } from "./.ldo/typeIndex.typings";
import { guaranteeFetch } from "@ldo/solid/dist/util/guaranteeFetch";
import type { TypeIndexProfile } from "./.ldo/profile.typings";
import { TypeIndexProfileShapeType } from "./.ldo/profile.shapeTypes";
import { TypeRegistrationShapeType } from "./.ldo/typeIndex.shapeTypes";
import { RDF_TYPE, TYPE_REGISTRATION } from "./constants";
interface GetInstanceUrisOptions {
solidLdoDataset?: SolidLdoDataset;
fetch?: typeof fetch;
}
import type { Options } from "./util/Options";
import { guaranteeOptions } from "./util/Options";
export async function getTypeRegistrations(
webId: string,
options?: GetInstanceUrisOptions,
options?: Options,
): Promise<TypeRegistration[]> {
const fetch = guaranteeFetch(options?.fetch);
const dataset = options?.solidLdoDataset ?? createSolidLdoDataset({ fetch });
const { dataset } = guaranteeOptions(options);
// Get Profile
const profileResource = dataset.getResource(webId);
const readResult = await profileResource.readIfUnfetched();
if (readResult.isError) throw readResult;
const profile = dataset
.usingType(TypeIndexProfileShapeType)
.fromSubject(webId);
const profile = await getProfile(webId, options);
// Get Type Indexes
const typeIndexUris = getTypeIndexesUrisFromProfile(profile);
@ -45,6 +34,17 @@ export async function getTypeRegistrations(
.matchSubject(RDF_TYPE, TYPE_REGISTRATION);
}
export async function getProfile(
webId: string,
options?: Options,
): Promise<TypeIndexProfile> {
const { dataset } = guaranteeOptions(options);
const profileResource = dataset.getResource(webId);
const readResult = await profileResource.readIfUnfetched();
if (readResult.isError) throw readResult;
return dataset.usingType(TypeIndexProfileShapeType).fromSubject(webId);
}
export function getTypeIndexesUrisFromProfile(
profile: TypeIndexProfile,
): LeafUri[] {
@ -61,10 +61,9 @@ export function getTypeIndexesUrisFromProfile(
export async function getInstanceUris(
classUri: string,
typeRegistrations: TypeRegistration[],
options?: GetInstanceUrisOptions,
options?: Options,
): Promise<LeafUri[]> {
const fetch = guaranteeFetch(options?.fetch);
const dataset = options?.solidLdoDataset ?? createSolidLdoDataset({ fetch });
const { dataset } = guaranteeOptions(options);
const leafUris = new Set<LeafUri>();
await Promise.all(

@ -0,0 +1,4 @@
export * from "./getTypeIndex";
export * from "./setTypeIndex";
export * from "./react/useInstanceUris";
export * from "./react/useTypeIndexProfile";

@ -0,0 +1,200 @@
import { v4 } from "uuid";
import {
TypeIndexDocumentShapeType,
TypeRegistrationShapeType,
} from "./.ldo/typeIndex.shapeTypes";
import { FOR_CLASS, RDF_TYPE, TYPE_REGISTRATION } from "./constants";
import { guaranteeOptions, type Options } from "./util/Options";
import { namedNode } from "@rdfjs/data-model";
import type { TypeRegistration } from "./.ldo/typeIndex.typings";
import { getProfile } from "./getTypeIndex";
import { TypeIndexProfileShapeType } from "./.ldo/profile.shapeTypes";
import type { SolidLdoDataset } from "@ldo/solid";
import type { Container } from "@ldo/solid";
/**
* =============================================================================
* INITIALIZERS
* =============================================================================
*/
export async function initTypeIndex(
webId: string,
options?: Options,
): Promise<void> {
const { dataset } = guaranteeOptions(options);
const profile = await getProfile(webId, options);
if (!profile.privateTypeIndex?.length || !profile.publicTypeIndex?.length) {
const profileFolder = await dataset.getResource(webId).getParentContainer();
if (profileFolder?.isError) throw profileFolder;
if (!profileFolder)
throw new Error("No folder to save the type indexes to.");
if (!profile.privateTypeIndex?.length) {
await createIndex(webId, profileFolder, dataset, true);
}
if (!profile.publicTypeIndex?.length) {
await createIndex(webId, profileFolder, dataset, true);
}
}
}
/**
* @internal
* @param webId
* @param profileFolder
* @param dataset
*/
export async function createIndex(
webId,
profileFolder: Container,
dataset: SolidLdoDataset,
isPrivate: boolean,
) {
// Create a private type index
const createResult = await profileFolder.createChildAndOverwrite(
`${isPrivate ? "private" : "public"}_index_${v4()}`,
);
if (createResult.isError) throw createResult;
const indexResource = createResult.resource;
const wacResult = await indexResource.setWac({
agent: {
[webId]: { read: true, write: true, append: true, control: true },
},
public: {
read: isPrivate ? false : true,
write: true,
append: true,
control: true,
},
authenticated: {
read: isPrivate ? false : true,
write: true,
append: true,
control: true,
},
});
if (wacResult.isError) throw wacResult;
const transaction = dataset.startTransaction();
const cProfile = transaction
.usingType(TypeIndexProfileShapeType)
.write(dataset.getResource(webId).uri)
.fromSubject(webId);
if (isPrivate) {
cProfile.privateTypeIndex?.push({ "@id": indexResource.uri });
} else {
cProfile.publicTypeIndex?.push({ "@id": indexResource.uri });
}
const cTypeIndex = transaction
.usingType(TypeIndexDocumentShapeType)
.write(indexResource.uri)
.fromSubject(indexResource.uri);
console.log(indexResource.uri, webId);
cTypeIndex.type = [{ "@id": "ListedDocument" }, { "@id": "TypeIndex" }];
console.log("added", transaction.getChanges().added?.toString());
console.log("removed", transaction.getChanges().added?.toString());
const commitResult = await transaction.commitToPod();
if (commitResult.isError) {
commitResult.errors.forEach((err) => {
if (err.type === "invalidUriError") {
console.log(err.uri);
}
});
throw commitResult;
}
}
/**
* =============================================================================
* DATASET MODIFIERS
* =============================================================================
*/
interface Instances {
instance?: string[];
instanceContainer?: string[];
}
export function addRegistration(
indexUri: string,
classUri: string,
instances: Instances,
options?: Options,
): void {
// Check to see if its already in the index
const typeRegistration = findAppropriateTypeRegistration(
indexUri,
classUri,
options,
);
// Add instances to type registration
instances.instance?.forEach((instance) => {
typeRegistration.instance?.push({ "@id": instance });
});
instances.instanceContainer?.forEach((instanceContainer) => {
typeRegistration.instanceContainer?.push({ "@id": instanceContainer });
});
}
export async function removeRegistration(
indexUri: string,
classUri: string,
instances: Instances,
options?: Options,
) {
// Check to see if its already in the index
const typeRegistration = findAppropriateTypeRegistration(
indexUri,
classUri,
options,
);
// Add instances to type registration
instances.instance?.forEach((instance) => {
typeRegistration.instance?.splice(
typeRegistration.instance.findIndex((val) => val["@id"] === instance),
1,
);
});
instances.instanceContainer?.forEach((instanceContainer) => {
typeRegistration.instance?.splice(
typeRegistration.instance.findIndex(
(val) => val["@id"] === instanceContainer,
),
1,
);
});
}
export function findAppropriateTypeRegistration(
indexUri: string,
classUri: string,
options?: Options,
) {
const { dataset } = guaranteeOptions(options);
// Check to see if its already in the index
const existingRegistrationUri: string | undefined = dataset
.match(
null,
namedNode(RDF_TYPE),
namedNode(TYPE_REGISTRATION),
namedNode(indexUri),
)
.match(null, namedNode(FOR_CLASS), namedNode(classUri))
.toArray()[0]?.subject.value;
let typeRegistration: TypeRegistration;
if (existingRegistrationUri) {
typeRegistration = dataset
.usingType(TypeRegistrationShapeType)
.write(indexUri)
.fromSubject(existingRegistrationUri);
} else {
typeRegistration = dataset.createData(
TypeRegistrationShapeType,
`${indexUri}#${v4()}`,
dataset.getResource(indexUri),
);
typeRegistration.type = { "@id": "TypeRegistration" };
typeRegistration.forClass = { "@id": classUri };
}
return typeRegistration;
}

@ -0,0 +1,13 @@
import { createSolidLdoDataset, type SolidLdoDataset } from "@ldo/solid";
import { guaranteeFetch } from "@ldo/solid/dist/util/guaranteeFetch";
export interface Options {
solidLdoDataset?: SolidLdoDataset;
fetch?: typeof fetch;
}
export function guaranteeOptions(options?: Options) {
const fetch = guaranteeFetch(options?.fetch);
const dataset = options?.solidLdoDataset ?? createSolidLdoDataset({ fetch });
return { fetch, dataset };
}

@ -2,18 +2,24 @@ import { createSolidLdoDataset } from "@ldo/solid";
import {
MY_BOOKMARKS_1_URI,
MY_BOOKMARKS_2_URI,
setupEmptyTypeIndex,
setupFullTypeIndex,
setUpServer,
WEB_ID,
} from "./setUpServer";
import { getInstanceUris, getTypeRegistrations } from "../src/getTypeIndex";
import { initTypeIndex } from "../src/setTypeIndex";
import { TypeIndexProfileShapeType } from "../src/.ldo/profile.shapeTypes";
// Use an increased timeout, since the CSS server takes too much setup time.
jest.setTimeout(40_000);
describe("General Tests", () => {
setUpServer();
const s = setUpServer();
it("gets the current typeindex", async () => {
await setupFullTypeIndex(s);
const solidLdoDataset = createSolidLdoDataset();
const typeRegistrations = await getTypeRegistrations(WEB_ID, {
solidLdoDataset,
@ -38,4 +44,20 @@ describe("General Tests", () => {
expect.arrayContaining([MY_BOOKMARKS_1_URI, MY_BOOKMARKS_2_URI]),
);
});
it("initializes the type index", async () => {
await setupEmptyTypeIndex(s);
const solidLdoDataset = createSolidLdoDataset();
await initTypeIndex(WEB_ID, { solidLdoDataset });
const profile = solidLdoDataset
.usingType(TypeIndexProfileShapeType)
.fromSubject(WEB_ID);
console.log(solidLdoDataset.toString());
expect(profile.privateTypeIndex?.["@id"]).toBeDefined();
expect(profile.publicTypeIndex?.["@id"]).toBeDefined();
});
});

@ -1,8 +0,0 @@
import { setUpServer } from "./setUpServer";
// Use an increased timeout, since the CSS server takes too much setup time.
jest.setTimeout(40_000);
describe("React Tests", () => {
setUpServer();
});

@ -53,6 +53,77 @@ export interface SetUpServerReturn {
>;
}
export async function setupFullTypeIndex(s: SetUpServerReturn) {
// Create a new document called sample.ttl
await s.authFetch(WEB_ID, { method: "DELETE" });
await s.authFetch(ROOT_CONTAINER, {
method: "POST",
headers: {
link: '<http://www.w3.org/ns/ldp#Container>; rel="type"',
slug: "myBookmarks/",
},
});
await Promise.all([
s.authFetch(PROFILE_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "card.ttl" },
body: PROFILE_TTL,
}),
s.authFetch(PROFILE_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "publicTypeIndex.ttl" },
body: PUBLIC_TYPE_INDEX_TTL,
}),
s.authFetch(PROFILE_CONTAINER, {
method: "POST",
headers: {
"content-type": "text/turtle",
slug: "privateTypeIndex.ttl",
},
body: PRIVATE_TYPE_INDEX_TTL,
}),
s.authFetch(MY_BOOKMARKS_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "bookmark1.ttl" },
body: "",
}),
s.authFetch(MY_BOOKMARKS_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "bookmark2.ttl" },
body: "",
}),
]);
}
export async function setupEmptyTypeIndex(s: SetUpServerReturn) {
// Create a new document called sample.ttl
await s.authFetch(WEB_ID, { method: "DELETE" });
await s.authFetch(ROOT_CONTAINER, {
method: "POST",
headers: {
link: '<http://www.w3.org/ns/ldp#Container>; rel="type"',
slug: "myBookmarks/",
},
});
await Promise.all([
s.authFetch(PROFILE_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "card.ttl" },
body: "",
}),
s.authFetch(MY_BOOKMARKS_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "bookmark1.ttl" },
body: "",
}),
s.authFetch(MY_BOOKMARKS_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "bookmark2.ttl" },
body: "",
}),
]);
}
export function setUpServer(): SetUpServerReturn {
// Ignore to build s
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -66,45 +137,6 @@ export function setUpServer(): SetUpServerReturn {
beforeEach(async () => {
s.fetchMock = jest.fn(s.authFetch);
// Create a new document called sample.ttl
await s.authFetch(WEB_ID, { method: "DELETE" });
await s.authFetch(ROOT_CONTAINER, {
method: "POST",
headers: {
link: '<http://www.w3.org/ns/ldp#Container>; rel="type"',
slug: "myBookmarks/",
},
});
await Promise.all([
s.authFetch(PROFILE_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "card.ttl" },
body: PROFILE_TTL,
}),
s.authFetch(PROFILE_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "publicTypeIndex.ttl" },
body: PUBLIC_TYPE_INDEX_TTL,
}),
s.authFetch(PROFILE_CONTAINER, {
method: "POST",
headers: {
"content-type": "text/turtle",
slug: "privateTypeIndex.ttl",
},
body: PRIVATE_TYPE_INDEX_TTL,
}),
s.authFetch(MY_BOOKMARKS_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "bookmark1.ttl" },
body: "",
}),
s.authFetch(MY_BOOKMARKS_CONTAINER, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "bookmark2.ttl" },
body: "",
}),
]);
});
afterEach(async () => {

@ -117,6 +117,8 @@ export class SolidLdoTransactionDataset
const changes = this.getChanges();
const changesByGraph = splitChangesByGraph(changes);
console.log(changesByGraph);
// Iterate through all changes by graph in
const results: [
GraphNode,
@ -138,6 +140,7 @@ export class SolidLdoTransactionDataset
];
}
if (isContainerUri(graph.value)) {
console.log(datasetChanges.removed?.toString());
return [
graph,
datasetChanges,

Loading…
Cancel
Save