Getting the root container works

main
Ailin Luca 2 years ago
parent 74880d09ff
commit 265b3e1c64
  1. 74
      package-lock.json
  2. 1
      packages/demo-react/package.json
  3. 6
      packages/demo-react/src/Layout.tsx
  4. 10
      packages/demo-react/src/dashboard/BuildRootContainer.ts
  5. 37
      packages/demo-react/src/dashboard/BuildRootContainer.tsx
  6. 41
      packages/demo-react/src/dashboard/Dashboard.tsx
  7. 2
      packages/solid-react/src/SolidAuthContext.ts
  8. 62
      packages/solid-react/src/SolidLdoProvider.tsx
  9. 20
      packages/solid-react/src/documentHooks/useAccessRules.ts
  10. 16
      packages/solid-react/src/documentHooks/useBinaryResource.ts
  11. 19
      packages/solid-react/src/documentHooks/useContainerResource.ts
  12. 16
      packages/solid-react/src/documentHooks/useDataResource.ts
  13. 27
      packages/solid-react/src/documentHooks/useDocument.ts
  14. 7
      packages/solid-react/src/index.ts
  15. 17
      packages/solid-react/src/useResource.ts
  16. 2
      packages/solid/package.json
  17. 2
      packages/solid/src/ResourceStore.ts
  18. 7
      packages/solid/src/SolidLdoDataset.ts
  19. 3
      packages/solid/src/index.ts
  20. 21
      packages/solid/src/requester/ContainerRequester.ts
  21. 35
      packages/solid/src/requester/requests/checkRootContainer.ts
  22. 49
      packages/solid/src/resource/Container.ts
  23. 18
      packages/solid/src/resource/Leaf.ts
  24. 5
      packages/solid/src/resource/Resource.ts
  25. 1
      packages/solid/src/resource/abstract/AbstractResource.ts
  26. 5
      packages/solid/src/resource/abstract/container/AbsentContainer.ts
  27. 3
      packages/solid/src/resource/abstract/container/AbstractContainer.ts
  28. 8
      packages/solid/src/resource/abstract/container/FetchedContainer.ts
  29. 8
      packages/solid/src/resource/abstract/container/PresentContainer.ts
  30. 5
      packages/solid/src/resource/abstract/container/UnfetchedContainer.ts
  31. 3
      packages/solid/src/resource/abstract/fetchStatus/Absent.ts
  32. 1
      packages/solid/src/resource/abstract/fetchStatus/Fetched.ts
  33. 3
      packages/solid/src/resource/abstract/fetchStatus/Present.ts
  34. 1
      packages/solid/src/resource/abstract/fetchStatus/Unfetched.ts
  35. 5
      packages/solid/src/resource/abstract/leaf/AbsentLeaf.ts
  36. 3
      packages/solid/src/resource/abstract/leaf/AbstractLeaf.ts
  37. 3
      packages/solid/src/resource/abstract/leaf/BinaryLeaf.ts
  38. 3
      packages/solid/src/resource/abstract/leaf/DataLeaf.ts
  39. 5
      packages/solid/src/resource/abstract/leaf/FetchedLeaf.ts
  40. 5
      packages/solid/src/resource/abstract/leaf/PresentLeaf.ts
  41. 5
      packages/solid/src/resource/abstract/leaf/UnfetchedLeaf.ts
  42. 14
      packages/solid/src/resource/error/MethodNotAllowedError.ts
  43. 6
      packages/solid/src/resource/error/ResourceError.ts
  44. 5
      packages/solid/src/util/rdfUtils.ts
  45. 87
      packages/solid/test/ContainerRequester.test.ts
  46. 6
      packages/solid/test/LeafRequester.test.ts

74
package-lock.json generated

@ -10962,11 +10962,22 @@
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
},
"node_modules/@types/parse-link-header": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/parse-link-header/-/parse-link-header-2.0.1.tgz",
"integrity": "sha512-BrKNSrRTqn3UkMXvdVtr/znJch0PMBpEvEP8oBkxDx7eEGntuFLI+WpA5HGsNHK4SlqyhaMa+Ks0ViwyixQB5w=="
},
"node_modules/@types/prettier": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
"integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA=="
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
"node_modules/@types/proper-lockfile": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@types/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
@ -11024,6 +11035,17 @@
"rdf-js": "^4.0.2"
}
},
"node_modules/@types/react": {
"version": "18.2.21",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz",
"integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/readable-stream": {
"version": "2.3.15",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz",
@ -11055,6 +11077,12 @@
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
},
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
},
"node_modules/@types/semver": {
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz",
@ -15207,6 +15235,12 @@
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
},
"node_modules/csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"dev": true
},
"node_modules/dag-map": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz",
@ -35583,6 +35617,7 @@
"@craco/craco": "^7.1.0",
"@ldo/cli": "^0.0.0",
"@types/jsonld": "^1.5.9",
"@types/react": "^18.2.21",
"@types/shexj": "^2.1.4",
"tsconfig-paths-webpack-plugin": "^4.1.0"
}
@ -36162,7 +36197,9 @@
"@ldo/dataset": "^0.0.0",
"@ldo/ldo": "^0.0.0",
"@ldo/rdf-utils": "^0.0.0",
"@types/parse-link-header": "^2.0.1",
"cross-fetch": "^3.1.6",
"http-link-header": "^1.1.1",
"ts-mixer": "^6.0.3"
},
"devDependencies": {
@ -46565,6 +46602,7 @@
"@ldo/cli": "^0.0.0",
"@ldo/solid-react": "^0.0.0",
"@types/jsonld": "^1.5.9",
"@types/react": "^18.2.21",
"@types/shexj": "^2.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -46931,8 +46969,10 @@
"@rdfjs/types": "^1.0.1",
"@solid/community-server": "^6.0.2",
"@types/jest": "^29.0.3",
"@types/parse-link-header": "^2.0.1",
"cross-fetch": "^3.1.6",
"dotenv": "^16.3.1",
"http-link-header": "^1.1.1",
"jest-rdf": "^1.8.0",
"ts-jest": "^29.0.2",
"ts-mixer": "^6.0.3",
@ -52050,11 +52090,22 @@
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
},
"@types/parse-link-header": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/parse-link-header/-/parse-link-header-2.0.1.tgz",
"integrity": "sha512-BrKNSrRTqn3UkMXvdVtr/znJch0PMBpEvEP8oBkxDx7eEGntuFLI+WpA5HGsNHK4SlqyhaMa+Ks0ViwyixQB5w=="
},
"@types/prettier": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
"integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA=="
},
"@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
"@types/proper-lockfile": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@types/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
@ -52112,6 +52163,17 @@
"rdf-js": "^4.0.2"
}
},
"@types/react": {
"version": "18.2.21",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz",
"integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"@types/readable-stream": {
"version": "2.3.15",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz",
@ -52143,6 +52205,12 @@
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
},
"@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
},
"@types/semver": {
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz",
@ -55254,6 +55322,12 @@
}
}
},
"csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"dev": true
},
"dag-map": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz",

@ -39,6 +39,7 @@
"@craco/craco": "^7.1.0",
"@ldo/cli": "^0.0.0",
"@types/jsonld": "^1.5.9",
"@types/react": "^18.2.21",
"@types/shexj": "^2.1.4",
"tsconfig-paths-webpack-plugin": "^4.1.0"
}

@ -4,11 +4,12 @@ import type { FunctionComponent } from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { Dashboard } from "./dashboard/Dashboard";
import { Media } from "./media/Media";
import { BuildRootContainer } from "./dashboard/BuildRootContainer";
const router = createBrowserRouter([
{
path: "/",
element: <Dashboard />,
element: <BuildRootContainer child={Dashboard} />,
},
{
path: "/media/:uri",
@ -16,13 +17,12 @@ const router = createBrowserRouter([
},
]);
const DEFAULT_ISSUER = "https://pod.lightover.com";
const DEFAULT_ISSUER = "https://solidweb.me";
export const Layout: FunctionComponent = () => {
const { login, logout, signUp, session, ranInitialAuthCheck } =
useSolidAuth();
const [issuer, setIssuer] = useState(DEFAULT_ISSUER);
console.log(ranInitialAuthCheck);
if (!ranInitialAuthCheck) {
return <p>Loading</p>;
}

@ -1,10 +0,0 @@
import type { FunctionComponent, PropsWithChildren, ReactNode } from "react";
import {} from "@ldo/solid";
interface BuildRootContainerChildProps {
rootContainer: ContainerResource
}
export const BuildRootContainer: FunctionComponent<{ children: FunctionComponent<BuildRootContainerChildProps> }> = () => {
};

@ -0,0 +1,37 @@
import React, { useState, useEffect } from "react";
import type { FunctionComponent } from "react";
import type { Container, LeafUri } from "@ldo/solid";
import { useSolidAuth, useLdo } from "@ldo/solid-react";
export interface BuildRootContainerChildProps {
rootContainer: Container;
}
export const BuildRootContainer: FunctionComponent<{
child: FunctionComponent<BuildRootContainerChildProps>;
}> = ({ child }) => {
const Child = child;
const [rootContainer, setRootContainer] = useState<Container | undefined>();
const { session } = useSolidAuth();
const { getResource } = useLdo();
useEffect(() => {
if (session.webId) {
const webIdResource = getResource(session.webId as LeafUri);
webIdResource.getRootContainer().then((rootContainer) => {
if (rootContainer.type !== "error") {
setRootContainer(rootContainer);
} else {
alert(rootContainer.message);
}
});
}
}, [session.webId]);
if (!session.webId || !rootContainer) {
// Return blank screen
return <p>Loading</p>;
}
return <Child rootContainer={rootContainer} />;
};

@ -1,29 +1,18 @@
import React, { useCallback, useEffect, useMemo } from "react";
import React from "react";
import type { FunctionComponent } from "react";
import { UploadButton } from "./UploadButton";
import { useContainerResource, useDataResource, useSolidAuth } from "@ldo/solid-react";
import { AccessRules, ContainerResource } from "@ldo/solid";
import type { BuildRootContainerChildProps } from "./BuildRootContainer";
export const Dashboard: FunctionComponent = () => {
const { session } = useSolidAuth();
const rootContainer = useRootContainer(session.webId);
const mainContainer = useContainerResource()
// useParentContainer
useEffect(() => {
if (rootContainer) {
}
}, [rootContainer]);
return (
<div>
<div>
<UploadButton />
</div>
<hr />
<div>{mainContainer.isLoading ? "Loading" : "Not Loading"}</div>
</div>
);
export const Dashboard: FunctionComponent<BuildRootContainerChildProps> = ({
rootContainer,
}) => {
return <p>{rootContainer.uri}</p>;
// return (
// <div>
// <div>
// <UploadButton />
// </div>
// <hr />
// <div>{mainContainer.isLoading ? "Loading" : "Not Loading"}</div>
// </div>
// );
};

@ -21,6 +21,6 @@ export interface SolidAuthFunctions {
// @ts-ignore
export const SolidAuthContext = createContext<SolidAuthFunctions>(undefined);
export function useSolidAuth() {
export function useSolidAuth(): SolidAuthFunctions {
return useContext(SolidAuthContext);
}

@ -3,43 +3,45 @@ import {
useMemo,
type FunctionComponent,
type PropsWithChildren,
useRef,
useEffect,
} from "react";
import { useSolidAuth } from "./SolidAuthContext";
import type { SolidLdoDataset, OnDocumentErrorCallback } from "@ldo/solid";
import type { SolidLdoDataset } from "@ldo/solid";
import { createSolidLdoDataset } from "@ldo/solid";
import type { LdoBase, ShapeType } from "@ldo/ldo";
import type { SubjectNode } from "@ldo/rdf-utils";
export const SolidLdoDatasetReactContext =
export const SolidLdoReactContext =
// This will be set in the provider
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
createContext<SolidLdoDataset>(undefined);
createContext<UseLdoResult>(undefined);
export function useSolidLdoDataset() {
return useContext(SolidLdoDatasetReactContext);
export interface UseLdoResult {
dataset: SolidLdoDataset;
getResource: SolidLdoDataset["getResource"];
getSubject<Type extends LdoBase>(
shapeType: ShapeType<Type>,
subject: string | SubjectNode,
): Type | Error;
}
export interface SolidLdoProviderProps extends PropsWithChildren {
onDocumentError?: OnDocumentErrorCallback;
export function useLdo(): UseLdoResult {
return useContext(SolidLdoReactContext);
}
export interface SolidLdoProviderProps extends PropsWithChildren {}
export const SolidLdoProvider: FunctionComponent<SolidLdoProviderProps> = ({
onDocumentError,
children,
}) => {
const { fetch } = useSolidAuth();
const curOnDocumentError = useRef(onDocumentError);
// Initialize storeDependencies before render
const solidLdoDataset = useMemo(() => {
const solidLdoDataset: SolidLdoDataset = useMemo(() => {
const ldoDataset = createSolidLdoDataset({
fetch,
});
if (curOnDocumentError.current) {
ldoDataset.onDocumentError(curOnDocumentError.current);
}
console.log("ldodatset1", ldoDataset);
return ldoDataset;
}, []);
@ -47,22 +49,24 @@ export const SolidLdoProvider: FunctionComponent<SolidLdoProviderProps> = ({
useEffect(() => {
solidLdoDataset.context.fetch = fetch;
}, [fetch]);
useEffect(() => {
if (curOnDocumentError.current) {
solidLdoDataset.offDocumentError(curOnDocumentError.current);
}
if (onDocumentError) {
solidLdoDataset.onDocumentError(onDocumentError);
curOnDocumentError.current = onDocumentError;
} else {
curOnDocumentError.current = undefined;
}
}, [onDocumentError]);
console.log("ldoDataset 2", solidLdoDataset);
const value: UseLdoResult = useMemo(
() => ({
dataset: solidLdoDataset,
getResource: solidLdoDataset.getResource.bind(solidLdoDataset),
getSubject<Type extends LdoBase>(
shapeType: ShapeType<Type>,
subject: string | SubjectNode,
): Type | Error {
return solidLdoDataset.usingType(shapeType).fromSubject(subject);
},
}),
[solidLdoDataset],
);
return (
<SolidLdoDatasetReactContext.Provider value={solidLdoDataset}>
<SolidLdoReactContext.Provider value={value}>
{children}
</SolidLdoDatasetReactContext.Provider>
</SolidLdoReactContext.Provider>
);
};

@ -1,20 +0,0 @@
import { useMemo } from "react";
import type { UseDocumentOptions } from "./useDocument";
import { useTrackStateUpdate } from "./useDocument";
import type { Resource } from "@ldo/solid";
import { useSolidLdoDataset } from "../SolidLdoProvider";
export function useAccessRules(
resource: string | Resource,
options?: UseDocumentOptions,
) {
const solidLdoDataset = useSolidLdoDataset();
const document = useMemo(() => {
return solidLdoDataset.getAccessRules(resource);
}, [resource, solidLdoDataset]);
useTrackStateUpdate(document, options);
return document;
}

@ -1,16 +0,0 @@
import { useMemo } from "react";
import { useSolidLdoDataset } from "../SolidLdoProvider";
import type { UseDocumentOptions } from "./useDocument";
import { useTrackStateUpdate } from "./useDocument";
export function useBinaryResource(uri: string, options?: UseDocumentOptions) {
const solidLdoDataset = useSolidLdoDataset();
const document = useMemo(() => {
return solidLdoDataset.getBinaryResource(uri);
}, [uri, solidLdoDataset]);
useTrackStateUpdate(document, options);
return document;
}

@ -1,19 +0,0 @@
import { useMemo } from "react";
import type { UseDocumentOptions } from "./useDocument";
import { useTrackStateUpdate } from "./useDocument";
import { useSolidLdoDataset } from "../SolidLdoProvider";
export function useContainerResource(
uri: string,
options?: UseDocumentOptions,
) {
const solidLdoDataset = useSolidLdoDataset();
const document = useMemo(() => {
return solidLdoDataset.getContainerResource(uri);
}, [uri, solidLdoDataset]);
useTrackStateUpdate(document, options);
return document;
}

@ -1,16 +0,0 @@
import { useMemo } from "react";
import { useSolidLdoDataset } from "../SolidLdoProvider";
import type { UseDocumentOptions } from "./useDocument";
import { useTrackStateUpdate } from "./useDocument";
export function useDataResource(uri: string, options?: UseDocumentOptions) {
const solidLdoDataset = useSolidLdoDataset();
const document = useMemo(() => {
return solidLdoDataset.getDataResource(uri);
}, [uri, solidLdoDataset]);
useTrackStateUpdate(document, options);
return document;
}

@ -1,27 +0,0 @@
import { useEffect } from "react";
import type { FetchableDocument } from "@ldo/solid";
import { useForceUpdate } from "../util/useForceReload";
export interface UseDocumentOptions {
suppressLoadOnMount: boolean;
}
export function useTrackStateUpdate(
document: FetchableDocument,
options?: UseDocumentOptions,
) {
const forceUpdate = useForceUpdate();
useEffect(() => {
// Set up the listener for state update
function onStateUpdateCallback() {
forceUpdate();
}
document.onStateUpdate(onStateUpdateCallback);
// Load the resource if load on mount is true
if (!options?.suppressLoadOnMount) {
document.read();
}
return () => document.offStateUpdate(onStateUpdateCallback);
}, []);
}

@ -1,8 +1,7 @@
export * from "./BrowserSolidLdoProvider";
export * from "./SolidAuthContext";
export { useLdo } from "./SolidLdoProvider";
// documentHooks
export * from "./documentHooks/useAccessRules";
export * from "./documentHooks/useBinaryResource";
export * from "./documentHooks/useContainerResource";
export * from "./documentHooks/useDataResource";
export * from "./useResource";

@ -0,0 +1,17 @@
import { useMemo } from "react";
import type {
Container,
ContainerUri,
LeafUri,
Resource,
Leaf,
} from "@ldo/solid";
import { useLdo } from "./SolidLdoProvider";
export function useResource(uri: ContainerUri): Container;
export function useResource(uri: LeafUri): Leaf;
export function useResource(uri: string): Resource;
export function useResource(uri: string): Resource {
const { getResource } = useLdo();
return useMemo(() => getResource(uri), [getResource, uri]);
}

@ -40,7 +40,9 @@
"@ldo/dataset": "^0.0.0",
"@ldo/ldo": "^0.0.0",
"@ldo/rdf-utils": "^0.0.0",
"@types/parse-link-header": "^2.0.1",
"cross-fetch": "^3.1.6",
"http-link-header": "^1.1.1",
"ts-mixer": "^6.0.3"
}
}

@ -33,7 +33,7 @@ export class ResourceStore {
if (isContainerUri(normalizedUri)) {
resource = new Container(normalizedUri, this.context);
} else {
resource = new Leaf(normalizedUri, this.context);
resource = new Leaf(normalizedUri as LeafUri, this.context);
}
this.resourceMap.set(normalizedUri, resource);
}

@ -19,9 +19,10 @@ export class SolidLdoDataset extends LdoDataset {
this.context = context;
}
get(uri: ContainerUri, options?: ResourceGetterOptions): Container;
get(uri: LeafUri, options?: ResourceGetterOptions): Leaf;
get(uri: string, options?: ResourceGetterOptions): Resource {
getResource(uri: ContainerUri, options?: ResourceGetterOptions): Container;
getResource(uri: LeafUri, options?: ResourceGetterOptions): Leaf;
getResource(uri: string, options?: ResourceGetterOptions): Resource;
getResource(uri: string, options?: ResourceGetterOptions): Resource {
return this.context.resourceStore.get(uri, options);
}
}

@ -4,3 +4,6 @@ export * from "./SolidLdoDatasetContext";
export * from "./resource/Resource";
export * from "./resource/Container";
export * from "./resource/Leaf";
export * from "./util/uriTypes";

@ -1,3 +1,22 @@
import { Requester } from "./Requester";
import type { CheckRootResult } from "./requests/checkRootContainer";
import { checkRootContainer } from "./requests/checkRootContainer";
export class ContainerRequester extends Requester {}
export const IS_ROOT_CONTAINER_KEY = "isRootContainer";
export class ContainerRequester extends Requester {
async isRootContainer(): Promise<CheckRootResult> {
return this.requestBatcher.queueProcess({
name: IS_ROOT_CONTAINER_KEY,
args: [{ uri: this.uri, fetch: this.context.fetch }],
perform: checkRootContainer,
modifyQueue: (queue, isLoading) => {
if (queue.length === 0) {
return isLoading[IS_ROOT_CONTAINER_KEY];
} else {
return queue[queue.length - 1].name === IS_ROOT_CONTAINER_KEY;
}
},
});
}
}

@ -0,0 +1,35 @@
import {
UnexpectedHttpError,
type HttpErrorResultType,
} from "../requestResults/HttpErrorResult";
import { UnexpectedError } from "../requestResults/ErrorResult";
import type { RequestParams } from "./requestParams";
import { parse as parseLinkHeader } from "http-link-header";
export type CheckRootResult = boolean | CheckRootResultError;
export type CheckRootResultError = HttpErrorResultType | UnexpectedError;
export async function checkRootContainer({
uri,
fetch,
}: Omit<RequestParams, "transaction">): Promise<CheckRootResult> {
try {
// Fetch options to determine the document type
const response = await fetch(uri, { method: "HEAD" });
const linkHeader = response.headers.get("link");
if (!linkHeader) {
return new UnexpectedHttpError(
uri,
response,
"No link header present in request.",
);
}
const parsedLinkHeader = parseLinkHeader(linkHeader);
const types = parsedLinkHeader.get("rel", "type");
return types.some(
(type) => type.uri === "http://www.w3.org/ns/pim/space#Storage",
);
} catch (err) {
return UnexpectedError.fromThrown(uri, err);
}
}

@ -1,22 +1,51 @@
import type { Requester } from "../requester/Requester";
import { ContainerRequester } from "../requester/ContainerRequester";
import { UnexpectedError } from "../requester/requestResults/ErrorResult";
import type { CheckRootResultError } from "../requester/requests/checkRootContainer";
import type { SolidLdoDatasetContext } from "../SolidLdoDatasetContext";
import { getParentUri } from "../util/rdfUtils";
import type { ContainerUri } from "../util/uriTypes";
import { Resource } from "./Resource";
export class Container extends Resource {
protected requester: Requester;
protected requester: ContainerRequester;
protected rootContainer: boolean | undefined;
readonly type = "container" as const;
constructor(uri: ContainerUri, context: SolidLdoDatasetContext) {
super(uri, context);
this.requester = new ContainerRequester(uri, context);
}
isRootContainer(): boolean | undefined {
return this.rootContainer;
}
getParentContainer(): Promise<Container | undefined> {
throw new Error("Method not implemented.");
}
getRootContainer(): Promise<Container> {
throw new Error("Method not implemented.");
async getRootContainer(): Promise<Container | CheckRootResultError> {
if (this.rootContainer === undefined) {
const rootContainerResult = await this.requester.isRootContainer();
if (typeof rootContainerResult !== "boolean") {
return rootContainerResult;
}
getMimeType(): string {
throw new Error("Method not implemented.");
this.rootContainer = rootContainerResult;
}
if (this.rootContainer) {
return this;
}
const parentUri = getParentUri(this.uri);
if (!parentUri) {
return new UnexpectedError(
this.uri,
new Error("Resource does not have a root container"),
);
}
return this.context.resourceStore.get(parentUri);
}
readonly uri: ContainerUri;
private rootContainer: boolean | undefined;
isRootContainer(): boolean | undefined {
return this.rootContainer;
getMimeType(): string {
throw new Error("Method not implemented.");
}
}

@ -1,20 +1,34 @@
import type { DatasetChanges } from "@ldo/rdf-utils";
import { LeafRequester } from "../requester/LeafRequester";
import type { Requester } from "../requester/Requester";
import type { CheckRootResultError } from "../requester/requests/checkRootContainer";
import type { UpdateResultError } from "../requester/requests/updateDataResource";
import type {
UploadResultError,
UploadResultWithoutOverwriteError,
} from "../requester/requests/uploadResource";
import type { SolidLdoDatasetContext } from "../SolidLdoDatasetContext";
import { getParentUri } from "../util/rdfUtils";
import type { LeafUri } from "../util/uriTypes";
import type { Container } from "./Container";
import { Resource } from "./Resource";
export class Leaf extends Resource {
protected requester: Requester;
readonly type = "leaf" as const;
constructor(uri: LeafUri, context: SolidLdoDatasetContext) {
super(uri, context);
this.requester = new LeafRequester(uri, context);
}
getParentContainer(): Promise<Container | undefined> {
throw new Error("Method not implemented.");
}
getRootContainer(): Promise<Container> {
throw new Error("Method not implemented.");
getRootContainer(): Promise<Container | CheckRootResultError> {
const parentUri = getParentUri(this.uri)!;
const parent = this.context.resourceStore.get(parentUri);
return parent.getRootContainer();
}
getMimeType(): string {
throw new Error("Method not implemented.");

@ -14,11 +14,13 @@ import type { DeleteResultError } from "../requester/requests/deleteResource";
import type { ReadResultError } from "../requester/requests/readResource";
import type { Container } from "./Container";
import type { Requester } from "../requester/Requester";
import type { CheckRootResultError } from "../requester/requests/checkRootContainer";
export abstract class Resource {
// All intance variables
protected readonly context: SolidLdoDatasetContext;
readonly uri: string;
abstract readonly type: string;
protected abstract readonly requester: Requester;
protected didInitialFetch: boolean = false;
protected absent: boolean | undefined;
@ -89,6 +91,7 @@ export abstract class Resource {
data: result.blob,
mimeType: result.mimeType,
};
return this;
default:
return new UnexpectedError(
this.uri,
@ -132,7 +135,7 @@ export abstract class Resource {
// Parent Container Methods -- Remember to change for Container
abstract getParentContainer(): Promise<Container | undefined>;
abstract getRootContainer(): Promise<Container>;
abstract getRootContainer(): Promise<Container | CheckRootResultError>;
// Exclusing Methods =========================================================
// Data Methods (Data Leaf Only)

@ -1 +0,0 @@
export abstract class AbstractResource {}

@ -1,5 +0,0 @@
import { Mixin } from "ts-mixer";
import { PresentContainer } from "./PresentContainer";
import { Absent } from "../fetchStatus/Absent";
export class AbsentContainer extends Mixin(PresentContainer, Absent) {}

@ -1,3 +0,0 @@
import { AbstractResource } from "../AbstractResource";
export abstract class AbstractContainer extends AbstractResource {}

@ -1,8 +0,0 @@
import { Mixin } from "ts-mixer";
import { AbstractContainer } from "./AbstractContainer";
import { Fetched } from "../fetchStatus/Fetched";
export abstract class FetchedContainer extends Mixin(
AbstractContainer,
Fetched,
) {}

@ -1,8 +0,0 @@
import { Mixin } from "ts-mixer";
import { FetchedContainer } from "./FetchedContainer";
import { Present } from "../fetchStatus/Present";
export abstract class PresentContainer extends Mixin(
FetchedContainer,
Present,
) {}

@ -1,5 +0,0 @@
import { Mixin } from "ts-mixer";
import { AbstractContainer } from "./AbstractContainer";
import { Unfetched } from "../fetchStatus/Unfetched";
export class UnfetchedContainer extends Mixin(AbstractContainer, Unfetched) {}

@ -1,3 +0,0 @@
import { Fetched } from "./Fetched";
export abstract class Absent extends Fetched {}

@ -1,3 +0,0 @@
import { Fetched } from "./Fetched";
export abstract class Present extends Fetched {}

@ -1,5 +0,0 @@
import { Mixin } from "ts-mixer";
import { Absent } from "../fetchStatus/Absent";
import { FetchedLeaf } from "./FetchedLeaf";
export class AbsentLeaf extends Mixin(FetchedLeaf, Absent) {}

@ -1,3 +0,0 @@
import { AbstractResource } from "../AbstractResource";
export abstract class AbstractLeaf extends AbstractResource {}

@ -1,3 +0,0 @@
import { PresentLeaf } from "./PresentLeaf";
export class BinaryLeaf extends PresentLeaf {}

@ -1,3 +0,0 @@
import { PresentLeaf } from "./PresentLeaf";
export class DataLeaf extends PresentLeaf {}

@ -1,5 +0,0 @@
import { Mixin } from "ts-mixer";
import { AbstractLeaf } from "./AbstractLeaf";
import { Fetched } from "../fetchStatus/Fetched";
export abstract class FetchedLeaf extends Mixin(AbstractLeaf, Fetched) {}

@ -1,5 +0,0 @@
import { Mixin } from "ts-mixer";
import { FetchedLeaf } from "./FetchedLeaf";
import { Present } from "../fetchStatus/Present";
export abstract class PresentLeaf extends Mixin(FetchedLeaf, Present) {}

@ -1,5 +0,0 @@
import { Mixin } from "ts-mixer";
import { AbstractLeaf } from "./AbstractLeaf";
import { Unfetched } from "../fetchStatus/Unfetched";
export class UnfetchedLeaf extends Mixin(AbstractLeaf, Unfetched) {}

@ -1,14 +0,0 @@
import type { ResourceType } from "../abstract/AbstractResource";
import type { LeafType } from "../abstract/leaf/Leaf";
import { ResourceError } from "./ResourceError";
export class MethodNotAllowedError<
rType extends ResourceType,
> extends ResourceError {
type: "MethodNotAllowed";
methodName: string;
currentResource: rType;
}
export class LeafMethodNotAllowedError extends MethodNotAllowedError<LeafType> {}
export class ContainerMethodNotAllowedError extends MethodNotAllowedError<LeafType> {}

@ -1,6 +0,0 @@
import type { ResourceType } from "../abstract/AbstractResource";
export abstract class ResourceError extends Error {
public abstract readonly currentResource: ResourceType;
public abstract readonly type: string;
}

@ -3,6 +3,7 @@ import { namedNode, quad as createQuad } from "@rdfjs/data-model";
import { DataResult } from "../requester/requestResults/DataResult";
import { TurtleFormattingError } from "../requester/requestResults/DataResult";
import type { Dataset } from "@rdfjs/types";
import type { ContainerUri } from "./uriTypes";
import { isContainerUri } from "./uriTypes";
const ldpContains = namedNode("http://www.w3.org/ns/ldp#contains");
@ -11,7 +12,7 @@ const ldpResource = namedNode("http://www.w3.org/ns/ldp#Resource");
const ldpContainer = namedNode("http://www.w3.org/ns/ldp#Container");
const ldpBasicContainer = namedNode("http://www.w3.org/ns/ldp#BasicContainer");
export function getParentUri(uri: string): string | undefined {
export function getParentUri(uri: string): ContainerUri | undefined {
const urlObject = new URL(uri);
const pathItems = urlObject.pathname.split("/");
if (
@ -25,7 +26,7 @@ export function getParentUri(uri: string): string | undefined {
}
pathItems.pop();
urlObject.pathname = `${pathItems.join("/")}/`;
return urlObject.toString();
return urlObject.toString() as ContainerUri;
}
export function getSlug(uri: string): string {

@ -0,0 +1,87 @@
import type { App } from "@solid/community-server";
import { getAuthenticatedFetch, ROOT_COONTAINER } from "./solidServer.helper";
import type { SolidLdoDataset } from "../src/SolidLdoDataset";
import { createSolidLdoDataset } from "../src/createSolidLdoDataset";
import { ContainerRequester } from "../src/requester/ContainerRequester";
describe("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_COONTAINER}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}`,
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);
});
});

@ -4,11 +4,9 @@ import type { SolidLdoDataset } from "../src/SolidLdoDataset";
import { createSolidLdoDataset } from "../src/createSolidLdoDataset";
import { LeafRequester } from "../src/requester/LeafRequester";
import { namedNode, quad as createQuad } from "@rdfjs/data-model";
import type { BinaryResult } from "../src/requester/requestResults/BinaryResult";
import { Readable } from "stream";
describe("Leaf Requester", () => {
let app: App;
let _app: App;
let authFetch: typeof fetch;
let fetchMock: typeof fetch;
let solidLdoDataset: SolidLdoDataset;
@ -25,7 +23,7 @@ describe("Leaf Requester", () => {
fetchMock = jest.fn(authFetch);
solidLdoDataset = createSolidLdoDataset({ fetch: fetchMock });
// Create a new document called sample.ttl
const [result] = await Promise.all([
await Promise.all([
authFetch(`${ROOT_COONTAINER}test_leaf/`, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "sample.ttl" },

Loading…
Cancel
Save