Added methods for the REsource Class

main
jaxoncreed 2 years ago
parent 0f68b88989
commit a9ba201bb2
  1. 10
      packages/demo-react/src/dashboard/BuildRootContainer.ts
  2. 36
      packages/demo-react/src/dashboard/Dashboard.tsx
  3. 2
      packages/solid/src/index.ts
  4. 33
      packages/solid/src/requester/LeafRequester.ts
  5. 4
      packages/solid/src/requester/requestResults/BinaryResult.ts
  6. 8
      packages/solid/src/requester/requests/createDataResource.ts
  7. 3
      packages/solid/src/requester/requests/deleteResource.ts
  8. 13
      packages/solid/src/requester/requests/readResource.ts
  9. 3
      packages/solid/src/requester/requests/updateDataResource.ts
  10. 10
      packages/solid/src/requester/requests/uploadResource.ts
  11. 86
      packages/solid/src/resource/Leaf.ts
  12. 184
      packages/solid/src/resource/Resource.ts
  13. 24
      packages/solid/src/util/RequestBatcher.ts

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

@ -7,39 +7,15 @@ import { AccessRules, ContainerResource } from "@ldo/solid";
export const Dashboard: FunctionComponent = () => {
const { session } = useSolidAuth();
const containerUri = useMemo(() => {
if (!session.webId) return "";
// HACK: this is a hard coded hack to find the root container. Solid doesn't
// have an official way of doing this.
const rootContainer = session.webId.replace("profile/card#me", "");
return `${rootContainer}demo-ldo/`;
}, [session.webId]);
const mainContainer = useDataResource(containerUri);
if (mainContainer instanceof AccessRules) {
console.log("here");
}
const rootContainer = useRootContainer(session.webId);
const mainContainer = useContainerResource()
// useParentContainer
useEffect(() => {
// Upon load check to see if the root folder exists
mainContainer.checkExists().then(async (doesExist) => {
console.log(doesExist);
const error: DocumentError = await mainContainer.create();
if (error) {
// hanldle
return;
}
if (rootContainer) {
// // If not, create it
// if (!doesExist) {
// await mainContainer.create();
// const accessRules = mainContainer.accessRules;
// }
});
}, [mainContainer]);
}
}, [rootContainer]);
return (
<div>

@ -2,5 +2,5 @@ export * from "./createSolidLdoDataset";
export * from "./SolidLdoDataset";
export * from "./SolidLdoDatasetContext";
export * from "./resource/Leaf";
export * from "./resource/Resource";
export * from "./resource/Container";

@ -1,5 +1,5 @@
import type { LeafUri } from "../util/uriTypes";
import { RequestBatcher } from "../util/RequestBatcher";
import { ANY_KEY, RequestBatcher } from "../util/RequestBatcher";
import type { SolidLdoDatasetContext } from "../SolidLdoDatasetContext";
import type { DatasetChanges } from "@ldo/rdf-utils";
import type {
@ -18,8 +18,14 @@ import type { DeleteResult } from "./requests/deleteResource";
import { deleteResource } from "./requests/deleteResource";
import type { UpdateResult } from "./requests/updateDataResource";
const READ_KEY = "read";
const CREATE_KEY = "createDataResource";
const UPLOAD_KEY = "upload";
const UPDATE_KEY = "updateDataREsource";
const DELETE_KEY = "delete";
export class LeafRequester {
private requestBatcher = new RequestBatcher();
private readonly requestBatcher = new RequestBatcher();
// All intance variables
readonly uri: LeafUri;
@ -30,11 +36,29 @@ export class LeafRequester {
this.context = context;
}
isLoading(): boolean {
return this.requestBatcher.isLoading(ANY_KEY);
}
isCreating(): boolean {
return this.requestBatcher.isLoading(CREATE_KEY);
}
isUploading(): boolean {
return this.requestBatcher.isLoading(UPLOAD_KEY);
}
isReading(): boolean {
return this.requestBatcher.isLoading(READ_KEY);
}
isUpdating(): boolean {
return this.requestBatcher.isLoading(UPDATE_KEY);
}
isDeletinng(): boolean {
return this.requestBatcher.isLoading(DELETE_KEY);
}
/**
* Read this resource.
*/
async read(): Promise<ReadResult> {
const READ_KEY = "read";
const transaction = this.context.solidLdoDataset.startTransaction();
const result = await this.requestBatcher.queueProcess({
name: READ_KEY,
@ -69,7 +93,6 @@ export class LeafRequester {
async createDataResource(
overwrite?: boolean,
): Promise<CreateResultWithoutOverwrite> {
const CREATE_KEY = "createDataResource";
const transaction = this.context.solidLdoDataset.startTransaction();
const result = await this.requestBatcher.queueProcess({
name: CREATE_KEY,
@ -119,7 +142,6 @@ export class LeafRequester {
mimeType: string,
overwrite?: boolean,
): Promise<UploadResultWithoutOverwrite | UploadResult> {
const UPLOAD_KEY = "upload";
const transaction = this.context.solidLdoDataset.startTransaction();
const result = await this.requestBatcher.queueProcess({
name: UPLOAD_KEY,
@ -157,7 +179,6 @@ export class LeafRequester {
* Delete this resource
*/
async delete(): Promise<DeleteResult> {
const DELETE_KEY = "delete";
const transaction = this.context.solidLdoDataset.startTransaction();
const result = await this.requestBatcher.queueProcess({
name: DELETE_KEY,

@ -3,10 +3,12 @@ import { RequesterResult } from "./RequesterResult";
export class BinaryResult extends RequesterResult {
type = "binary" as const;
readonly blob: Blob;
readonly mimeType: string;
constructor(uri: string, blob: Blob) {
constructor(uri: string, blob: Blob, mimeType: string) {
super(uri);
this.blob = blob;
this.mimeType = mimeType;
}
static is(response: Response): boolean {

@ -13,11 +13,15 @@ import { deleteResource } from "./deleteResource";
import { readResource } from "./readResource";
import type { RequestParams } from "./requestParams";
export type CreateResult = DataResult | HttpErrorResultType | UnexpectedError;
export type CreateResult = DataResult | CreateResultErrors;
export type CreateResultErrors = HttpErrorResultType | UnexpectedError;
export type CreateResultWithoutOverwrite =
| CreateResult
| TurtleFormattingError
| CreateResultWithoutOverwriteErrors
| BinaryResult;
export type CreateResultWithoutOverwriteErrors =
| TurtleFormattingError
| CreateResultErrors;
export function createDataResource(
params: RequestParams,

@ -6,7 +6,8 @@ import { UnexpectedHttpError } from "../requestResults/HttpErrorResult";
import type { RequestParams } from "./requestParams";
import { deleteResourceRdfFromContainer } from "../../util/rdfUtils";
export type DeleteResult = AbsentResult | HttpErrorResultType | UnexpectedError;
export type DeleteResult = AbsentResult | DeleteResultError;
export type DeleteResultError = HttpErrorResultType | UnexpectedError;
export async function deleteResource({
uri,

@ -2,6 +2,7 @@ import { DataResult } from "../requestResults/DataResult";
import type { TurtleFormattingError } from "../requestResults/DataResult";
import {
HttpErrorResult,
ServerHttpError,
type HttpErrorResultType,
} from "../requestResults/HttpErrorResult";
import { UnexpectedError } from "../requestResults/ErrorResult";
@ -17,6 +18,8 @@ export type ReadResult =
| AbsentResult
| DataResult
| BinaryResult
| ReadResultError;
export type ReadResultError =
| HttpErrorResultType
| TurtleFormattingError
| UnexpectedError;
@ -44,8 +47,16 @@ export async function readResource({
return addRawTurtleToDataset(rawTurtle, transaction, uri);
} else {
// Load Blob
const contentType = response.headers.get("content-type");
if (!contentType) {
return new ServerHttpError(
uri,
response,
"Server provided no content-type",
);
}
const blob = await response.blob();
return new BinaryResult(uri, blob);
return new BinaryResult(uri, blob, contentType);
}
} catch (err) {
return UnexpectedError.fromThrown(uri, err);

@ -4,7 +4,8 @@ import type { HttpErrorResultType } from "../requestResults/HttpErrorResult";
import type { UnexpectedError } from "../requestResults/ErrorResult";
import type { RequestParams } from "./requestParams";
export type UpdateResult = DataResult | HttpErrorResultType | UnexpectedError;
export type UpdateResult = DataResult | UpdateResultError;
export type UpdateResultError = HttpErrorResultType | UnexpectedError;
export async function updateDataResource(
_params: RequestParams,

@ -17,11 +17,15 @@ import { deleteResource } from "./deleteResource";
import { readResource } from "./readResource";
import type { RequestParams } from "./requestParams";
export type UploadResult = BinaryResult | HttpErrorResultType | UnexpectedError;
export type UploadResult = BinaryResult | UploadResultError;
export type UploadResultError = HttpErrorResultType | UnexpectedError;
export type UploadResultWithoutOverwrite =
| UploadResult
| TurtleFormattingError
| UploadResultWithoutOverwriteError
| DataResult;
export type UploadResultWithoutOverwriteError =
| UploadResultError
| TurtleFormattingError;
export function uploadResource(
params: RequestParams,
@ -78,7 +82,7 @@ export async function uploadResource(
if (httpError) return httpError;
addResourceRdfToContainer(uri, transaction);
return new BinaryResult(uri, blob);
return new BinaryResult(uri, blob, mimeType);
} catch (err) {
return UnexpectedError.fromThrown(uri, err);
}

@ -1,86 +0,0 @@
// import type { LdoDataset } from "@ldo/ldo";
// import type { LeafMethodNotAllowedError } from "./error/MethodNotAllowedError";
// import type { DatasetChanges } from "@ldo/rdf-utils";
// import type { PresentContainer } from "./abstract/container/PresentContainer";
import type { SolidLdoDatasetContext } from "../SolidLdoDatasetContext";
import type { LeafUri } from "../uriTypes";
import { Resource } from "./Resource";
export interface ConcreteInstance {
uri: LeafUri;
context: SolidLdoDatasetContext;
// methods: typeof AbstractLeaf;
}
// REMEMBER: This file should be replaced with non abstract methods
export class Leaf extends Resource {
// All intance variables
private readonly i: SolidLdoDatasetContext;
uri: string;
abstract type(): LeafType["type"];
// Loading Methods
isLoading(): boolean {
return (
this.isCreating() ||
this.isReading() ||
this.isUpdating() ||
this.isDeletinng()
);
}
abstract isCreating(): boolean;
abstract isReading(): boolean;
abstract isUpdating(): boolean;
abstract isDeletinng(): boolean;
isDoingInitialFetch(): boolean {
return this.isReading() && !this.didInitialFetch();
}
// Checkers
abstract didInitialFetch(): boolean;
abstract isFetched(): boolean;
abstract isUnfetched(): boolean;
abstract isBinary: boolean | undefined;
abstract isDataResource(): boolean | undefined;
// Read Methods
abstract read(): Promise<PresentLeafType | LdoSolidError>;
abstract readIfUnfetched(): Promise<PresentLeafType | LdoSolidError>;
// Create Methods
abstract createOrOverwrite(): Promise<DataLeaf | LdoSolidError>;
abstract createOrOverwrite(blob: Blob): Promise<BinaryLeaf | LdoSolidError>;
abstract createIfAbsent(): Promise<LeafType | LdoSolidError>;
abstract createIfAbsent(blob: Blob): Promise<LeafType | LdoSolidError>;
// Delete Method
abstract delete(): Promise<AbsentLeaf | LdoSolidError>;
// Parent Container Methods -- Remember to change for Container
abstract getCachedParentContainer(): ContainerType | LdoSolidError;
abstract getParentContainer(): Promise<PresentContainer | LdoSolidError>;
abstract reloadParentContainer(): Promise<PresentContainer | LdoSolidError>;
abstract getRootContainerFromCache():
| ContainerType
| undefined
| LdoSolidError;
abstract getRootContainer(): Promise<
FetchedContainerType | undefined | LdoSolidError
>;
abstract getRootContainerFromPod(): Promise<
FetchedContainerType | undefined | LdoSolidError
>;
// Exclusing Methods =========================================================
// Data Methods (Data Leaf Only)
abstract getLdoDataset(): LdoDataset | LeafMethodNotAllowedError;
abstract reloadLdoDataset(): Promise<LdoDataset | LeafMethodNotAllowedError>;
abstract hasData(): boolean | LeafMethodNotAllowedError;
abstract reloadHasData(): Promise<boolean | LeafMethodNotAllowedError>;
abstract update(
changes: DatasetChanges,
): Promise<DataLeaf | LdoSolidError | LeafMethodNotAllowedError>;
// Binary Methods (Binary Only)
abstract getMimeType(): string | LeafMethodNotAllowedError;
abstract reloadMimeType(): Promise<string | LeafMethodNotAllowedError>;
// Create Methods (AbsentLeaf Only)
abstract create(): Promise<
DataLeaf | LdoSolidError | LeafMethodNotAllowedError
>;
abstract create(
blob: Blob,
): Promise<BinaryLeaf | LdoSolidError | LeafMethodNotAllowedError>;
}

@ -1 +1,183 @@
export class Resource {}
// import type { LdoDataset } from "@ldo/ldo";
// import type { LeafMethodNotAllowedError } from "./error/MethodNotAllowedError";
// import type { DatasetChanges } from "@ldo/rdf-utils";
// import type { PresentContainer } from "./abstract/container/PresentContainer";
import type { SolidLdoDatasetContext } from "../SolidLdoDatasetContext";
import { LeafRequester } from "../requester/LeafRequester";
import type { AbsentResult } from "../requester/requestResults/AbsentResult";
import type { BinaryResult } from "../requester/requestResults/BinaryResult";
import type { DataResult } from "../requester/requestResults/DataResult";
import {
UnexpectedError,
type ErrorResult,
} from "../requester/requestResults/ErrorResult";
import type {
CreateResultErrors,
CreateResultWithoutOverwriteErrors,
} from "../requester/requests/createDataResource";
import type { ReadResultError } from "../requester/requests/readResource";
import type { UploadResultError } from "../requester/requests/uploadResource";
import type { LeafUri } from "../uriTypes";
export interface ConcreteInstance {
uri: LeafUri;
context: SolidLdoDatasetContext;
// methods: typeof AbstractLeaf;
}
// REMEMBER: This file should be replaced with non abstract methods
export class Resource {
// All intance variables
private readonly context: SolidLdoDatasetContext;
readonly uri: string;
private readonly requester: LeafRequester;
private didInitialFetch: boolean = false;
private absent: boolean | undefined;
private binaryData: { data: Blob; mimeType: string } | undefined;
constructor(uri: string, context: SolidLdoDatasetContext) {
this.uri = uri;
this.context = context;
this.requester = new LeafRequester(uri as LeafUri, context);
}
// Loading Methods
isLoading(): boolean {
return this.requester.isLoading();
}
isCreating(): boolean {
return this.requester.isCreating();
}
isUploading(): boolean {
return this.requester.isUploading();
}
isReading(): boolean {
return this.requester.isReading();
}
isUpdating(): boolean {
return this.requester.isUpdating();
}
isDeleting(): boolean {
return this.requester.isDeletinng();
}
isDoingInitialFetch(): boolean {
return this.isReading() && !this.isFetched();
}
// Checkers
isFetched(): boolean {
return this.didInitialFetch;
}
isUnfetched(): boolean {
return !this.didInitialFetch;
}
isBinary(): boolean | undefined {
if (!this.didInitialFetch) {
return undefined;
}
return !!this.binaryData;
}
isDataResource(): boolean | undefined {
if (!this.didInitialFetch) {
return undefined;
}
return !this.binaryData;
}
private parseResult(
result: AbsentResult | BinaryResult | DataResult | ErrorResult,
) {
switch (result.type) {
case "error":
return result;
case "absent":
this.didInitialFetch = true;
this.absent = true;
delete this.binaryData;
return this;
case "data":
this.didInitialFetch = true;
this.absent = false;
delete this.binaryData;
return this;
case "binary":
this.didInitialFetch = true;
this.absent = false;
this.binaryData = {
data: result.blob,
mimeType: result.mimeType,
};
default:
return new UnexpectedError(
this.uri,
new Error("Unknown request result"),
);
}
}
// Read Methods
async read(): Promise<Resource | ReadResultError> {
return this.parseResult(await this.requester.read()) as
| Resource
| ReadResultError;
}
async readIfUnfetched(): Promise<Resource | ReadResultError> {
if (this.didInitialFetch) {
return this;
}
return this.read();
}
// Create Methods
async createAndOverwrite(): Promise<Resource | CreateResultErrors> {
return this.parseResult(await this.requester.createDataResource(true)) as
| Resource
| CreateResultErrors;
}
async createIfAbsent(): Promise<
Resource | CreateResultWithoutOverwriteErrors
> {
return this.parseResult(await this.requester.createDataResource()) as
| Resource
| CreateResultWithoutOverwriteErrors;
}
async uploadAndOverwrite(
blob: Blob,
mimeType: string,
): Promise<Resource | UploadResultError> {
return this.parseResult(await this.requester.upload(blob, mimeType)) as
| Resource
| UploadResultError;
}
abstract createIfAbsent(blob: Blob): Promise<LeafType | LdoSolidError>;
// Delete Method
abstract delete(): Promise<AbsentLeaf | LdoSolidError>;
// Parent Container Methods -- Remember to change for Container
abstract getCachedParentContainer(): ContainerType | LdoSolidError;
abstract getParentContainer(): Promise<PresentContainer | LdoSolidError>;
abstract reloadParentContainer(): Promise<PresentContainer | LdoSolidError>;
abstract getRootContainerFromCache():
| ContainerType
| undefined
| LdoSolidError;
abstract getRootContainer(): Promise<
FetchedContainerType | undefined | LdoSolidError
>;
abstract getRootContainerFromPod(): Promise<
FetchedContainerType | undefined | LdoSolidError
>;
// Exclusing Methods =========================================================
// Data Methods (Data Leaf Only)
abstract getLdoDataset(): LdoDataset | LeafMethodNotAllowedError;
abstract reloadLdoDataset(): Promise<LdoDataset | LeafMethodNotAllowedError>;
abstract hasData(): boolean | LeafMethodNotAllowedError;
abstract reloadHasData(): Promise<boolean | LeafMethodNotAllowedError>;
abstract update(
changes: DatasetChanges,
): Promise<DataLeaf | LdoSolidError | LeafMethodNotAllowedError>;
// Binary Methods (Binary Only)
abstract getMimeType(): string | LeafMethodNotAllowedError;
abstract reloadMimeType(): Promise<string | LeafMethodNotAllowedError>;
}

@ -7,6 +7,8 @@ export interface WaitingProcess<Args extends any[], Return> {
awaitingRejections: ((err: any) => void)[];
}
export const ANY_KEY = "any";
export interface WaitingProcessOptions<Args extends any[], Return> {
name: string;
args: Args;
@ -27,7 +29,7 @@ export interface WaitingProcessOptions<Args extends any[], Return> {
export class RequestBatcher {
private lastRequestTimestampMap: Record<string, number> = {};
private isLoading: Record<string, boolean> = {};
private loadingMap: Record<string, boolean> = {};
private isWaiting: boolean = false;
private processQueue: WaitingProcess<any[], any>[] = [];
public shouldBatchAllRequests: boolean;
@ -43,12 +45,16 @@ export class RequestBatcher {
this.batchMillis = options?.batchMillis || 1000;
}
public isLoading(key: string): boolean {
return !!this.loadingMap[key];
}
private triggerOrWaitProcess() {
if (!this.processQueue[0]) {
return;
}
const processName = this.shouldBatchAllRequests
? "any"
? ANY_KEY
: this.processQueue[0].name;
// Set last request timestamp if not available
@ -62,7 +68,7 @@ export class RequestBatcher {
const triggerProcess = async () => {
this.isWaiting = false;
this.lastRequestTimestampMap[processName] = Date.now();
this.lastRequestTimestampMap["any"] = Date.now();
this.lastRequestTimestampMap[ANY_KEY] = Date.now();
const processToTrigger = this.processQueue.shift();
if (processToTrigger) {
try {
@ -84,13 +90,13 @@ export class RequestBatcher {
(process) => process.name === processToTrigger.name,
)
) {
this.isLoading[processToTrigger.name] = false;
this.loadingMap[processToTrigger.name] = false;
}
if (this.processQueue.length > 0) {
this.triggerOrWaitProcess();
} else {
this.isLoading["any"] = false;
this.loadingMap[ANY_KEY] = false;
}
}
};
@ -111,7 +117,11 @@ export class RequestBatcher {
this.processQueue[this.processQueue.length - 1];
if (lastProcessInQueue) {
const didModifyLast = lastProcessInQueue
? options.modifyQueue(this.processQueue, this.isLoading, options.args)
? options.modifyQueue(
this.processQueue,
this.loadingMap,
options.args,
)
: false;
if (didModifyLast) {
lastProcessInQueue.awaitingResolutions.push(resolve);
@ -130,7 +140,7 @@ export class RequestBatcher {
this.processQueue.push(
waitingProcess as unknown as WaitingProcess<any[], any>,
);
this.isLoading[waitingProcess.name] = true;
this.loadingMap[waitingProcess.name] = true;
this.triggerOrWaitProcess();
});
}

Loading…
Cancel
Save