parent
74880d09ff
commit
265b3e1c64
@ -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>
|
||||
// );
|
||||
}; |
||||
|
@ -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]); |
||||
} |
@ -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; |
||||
} |
||||
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); |
||||
} |
||||
|
||||
getMimeType(): string { |
||||
throw new Error("Method not implemented."); |
||||
} |
||||
readonly uri: ContainerUri; |
||||
private rootContainer: boolean | undefined; |
||||
|
||||
isRootContainer(): boolean | undefined { |
||||
return this.rootContainer; |
||||
} |
||||
} |
||||
|
@ -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 +0,0 @@ |
||||
export abstract class Fetched {} |
@ -1,3 +0,0 @@ |
||||
import { Fetched } from "./Fetched"; |
||||
|
||||
export abstract class Present extends Fetched {} |
@ -1 +0,0 @@ |
||||
export abstract class Unfetched {} |
@ -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; |
||||
} |
@ -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); |
||||
}); |
||||
}); |
Loading…
Reference in new issue