From 1ce4e46d501b38c742998883b736331013e3b8fd Mon Sep 17 00:00:00 2001 From: Ailin Luca Date: Wed, 20 Sep 2023 23:37:24 -0400 Subject: [PATCH] Fixed problem with objects being overwritten --- .../src/dashboard/BuildMainContainer.tsx | 16 +++++----- .../demo-react/src/dashboard/UploadButton.tsx | 30 ++++++++++++++----- .../src/util/addObjectToDataset.ts | 7 +++-- .../test/jsonldDatasetProxy.test.ts | 14 +++++++-- packages/solid-react/src/useLdoMethods.ts | 5 ++-- packages/solid/src/SolidLdoDataset.ts | 1 + .../requester/requests/createDataResource.ts | 1 + .../src/requester/requests/getAccessRules.ts | 4 +-- .../requester/requests/updateDataResource.ts | 8 +++-- .../src/requester/requests/uploadResource.ts | 8 +++-- .../results/error/HttpErrorResult.ts | 6 +++- packages/solid/src/resource/Container.ts | 4 +-- packages/solid/src/resource/Leaf.ts | 4 +-- packages/solid/src/resource/Resource.ts | 2 +- 14 files changed, 72 insertions(+), 38 deletions(-) diff --git a/packages/demo-react/src/dashboard/BuildMainContainer.tsx b/packages/demo-react/src/dashboard/BuildMainContainer.tsx index 611bd40..94ea231 100644 --- a/packages/demo-react/src/dashboard/BuildMainContainer.tsx +++ b/packages/demo-react/src/dashboard/BuildMainContainer.tsx @@ -18,18 +18,16 @@ export const BuildMainContainer: FunctionComponent<{ useEffect(() => { if (session.webId) { const webIdResource = getResource(session.webId as LeafUri); - webIdResource.getRootContainer().then(async (rootContainerResult) => { - if (rootContainerResult.isError) { - alert(rootContainerResult.message); + webIdResource.getRootContainer().then(async (rootContainer) => { + if (rootContainer.isError) { + alert(rootContainer.message); return; } - const mainContainer = getResource( - `${rootContainerResult.rootContainer.uri}demo-react/`, - ); + const mainContainer = getResource(`${rootContainer.uri}demo-react/`); setMainContainer(mainContainer); - await mainContainer.read(); - if (mainContainer.isAbsent()) { - await mainContainer.createIfAbsent(); + const createResult = await mainContainer.createIfAbsent(); + // Only set the access rules if the create was a success. + if (createResult.type === "createSuccess") { await mainContainer.setAccessRules({ public: { read: true, diff --git a/packages/demo-react/src/dashboard/UploadButton.tsx b/packages/demo-react/src/dashboard/UploadButton.tsx index 298618f..e0830d0 100644 --- a/packages/demo-react/src/dashboard/UploadButton.tsx +++ b/packages/demo-react/src/dashboard/UploadButton.tsx @@ -1,9 +1,10 @@ import React, { useCallback, useState, useRef } from "react"; import type { FunctionComponent, FormEvent } from "react"; -import type { Container, Leaf } from "@ldo/solid"; +import type { Container, Leaf, LeafUri } from "@ldo/solid"; import { v4 } from "uuid"; import { useLdo, useSolidAuth } from "@ldo/solid-react"; import { PostShShapeType } from "../.ldo/post.shapeTypes"; +import { transactionChanges } from "@ldo/ldo"; export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({ mainContainer, @@ -18,28 +19,29 @@ export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({ e.preventDefault(); // Create the container file - const mediaContainer = await mainContainer.createChildAndOverwrite( + const mediaContainerResult = await mainContainer.createChildAndOverwrite( `${v4()}/`, ); - if (mediaContainer.type === "error") { - alert(mediaContainer.message); + if (mediaContainerResult.isError) { + alert(mediaContainerResult.message); return; } + const mediaContainer = mediaContainerResult.resource; // Upload Image let uploadedImage: Leaf | undefined; if (selectedFile) { const result = await mediaContainer.uploadChildAndOverwrite( - selectedFile.name, + selectedFile.name as LeafUri, selectedFile, selectedFile.type, ); - if (result.type === "error") { + if (result.isError) { alert(result.message); await mediaContainer.delete(); return; } - uploadedImage = result; + uploadedImage = result.resource; } // Create Post @@ -49,6 +51,12 @@ export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({ indexResource.uri, indexResource, ); + console.log("Created Data"); + const changes = transactionChanges(post); + console.log("added"); + console.log(changes.added?.toString()); + console.log("removed"); + console.log(changes.removed?.toString()); post.articleBody = message; if (uploadedImage) { post.image = { "@id": uploadedImage.uri }; @@ -56,10 +64,16 @@ export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({ if (session.webId) { post.publisher = { "@id": session.webId }; } + console.log("Created Data 2"); + const changes2 = transactionChanges(post); + console.log("added"); + console.log(changes2.added?.toString()); + console.log("removed"); + console.log(changes2.removed?.toString()); post.type = { "@id": "SocialMediaPosting" }; post.uploadDate = new Date().toISOString(); const result = await commitData(post); - if (result.type === "error") { + if (result.isError) { alert(result.message); } diff --git a/packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts b/packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts index 7252503..a24e437 100644 --- a/packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts +++ b/packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts @@ -47,9 +47,10 @@ export function addRawValueToDatasetRecursive( }); } else { // Delete any triples if the id is the same - if (!visitedObjects.has(object) && !isSubjectProxy(value)) { - dataset.deleteMatches(object, undefined, undefined); - } + // if (!visitedObjects.has(object) && !isSubjectProxy(value)) { + // console.log("deleting 2", object.value); + // dataset.deleteMatches(object, undefined, undefined); + // } proxyContext.writeGraphs.forEach((graph) => { dataset.add(quad(subject, predicate, object, graph)); }); diff --git a/packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts b/packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts index a38b7e6..b1d723c 100644 --- a/packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts +++ b/packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts @@ -581,6 +581,14 @@ describe("jsonldDatasetProxy", () => { ); }); + it("Does not overwrite the full object when a partial object is provided", async () => { + const [dataset, observation] = await getTinyLoadedDataset(); + observation.subject = { "@id": "http://example.com/Patient2" }; + expect(dataset.toString()).toBe( + ' .\n "Garrett" .\n .\n "Rob" .\n .\n', + ); + }); + it("Does not remove the full object when it is replaced on an array", async () => { const [dataset, observation] = await getTinyLoadedDataset(); const replacementPatient: PatientShape = { @@ -643,7 +651,7 @@ describe("jsonldDatasetProxy", () => { const [dataset, observation] = await getTinyLoadedDatasetWithBlankNodes(); delete observation.subject?.roommate?.[0]; expect(dataset.toString()).toBe( - ' _:b25_Patient1 .\n_:b25_Patient1 "Garrett" .\n', + ' _:b26_Patient1 .\n_:b26_Patient1 "Garrett" .\n', ); }); @@ -699,7 +707,7 @@ describe("jsonldDatasetProxy", () => { expect(observation[Symbol.search]).toBe(undefined); }); - it("Removes old triples from a node that has the same id as the one it replaced", async () => { + it("Only replaces referenced triples on a node that has the same id as the one it replaced", async () => { const [dataset, observation] = await getTinyLoadedDataset(); const replacementPatient: PatientShape = { "@id": "http://example.com/Patient1", @@ -707,7 +715,7 @@ describe("jsonldDatasetProxy", () => { }; observation.subject = replacementPatient; expect(dataset.toString()).toBe( - ' .\n "Mister Sneaky" .\n "Rob" .\n .\n', + ' .\n "Mister Sneaky" .\n .\n "Rob" .\n .\n', ); }); diff --git a/packages/solid-react/src/useLdoMethods.ts b/packages/solid-react/src/useLdoMethods.ts index 102d94c..e922605 100644 --- a/packages/solid-react/src/useLdoMethods.ts +++ b/packages/solid-react/src/useLdoMethods.ts @@ -2,8 +2,9 @@ import type { LdoBase, ShapeType } from "@ldo/ldo"; import { transactionChanges } from "@ldo/ldo"; import { write } from "@ldo/ldo"; import { startTransaction } from "@ldo/ldo"; -import type { SubjectNode } from "@ldo/rdf-utils"; +import type { DatasetChanges, SubjectNode } from "@ldo/rdf-utils"; import type { Resource, SolidLdoDataset } from "@ldo/solid"; +import type { Quad } from "@rdfjs/types"; export interface UseLdoMethods { dataset: SolidLdoDataset; @@ -85,7 +86,7 @@ export function createUseLdoMethods(dataset: SolidLdoDataset): UseLdoMethods { input: LdoBase, ): ReturnType { const changes = transactionChanges(input); - return dataset.commitChangesToPod(changes); + return dataset.commitChangesToPod(changes as DatasetChanges); }, }; } diff --git a/packages/solid/src/SolidLdoDataset.ts b/packages/solid/src/SolidLdoDataset.ts index 34d8012..c7d5da7 100644 --- a/packages/solid/src/SolidLdoDataset.ts +++ b/packages/solid/src/SolidLdoDataset.ts @@ -44,6 +44,7 @@ export class SolidLdoDataset extends LdoDataset { | AggregateError > { const changesByGraph = splitChangesByGraph(changes); + console.log(changesByGraph); const results: [ GraphNode, DatasetChanges, diff --git a/packages/solid/src/requester/requests/createDataResource.ts b/packages/solid/src/requester/requests/createDataResource.ts index b9d848a..66f4e76 100644 --- a/packages/solid/src/requester/requests/createDataResource.ts +++ b/packages/solid/src/requester/requests/createDataResource.ts @@ -109,6 +109,7 @@ export async function createDataResource( } else { // Perform a read to check if it exists const readResult = await readResource(uri, options); + // If it does exist stop and return. if (readResult.type !== "absentReadSuccess") { return readResult; diff --git a/packages/solid/src/requester/requests/getAccessRules.ts b/packages/solid/src/requester/requests/getAccessRules.ts index 834bd88..bcfb6dc 100644 --- a/packages/solid/src/requester/requests/getAccessRules.ts +++ b/packages/solid/src/requester/requests/getAccessRules.ts @@ -1,6 +1,4 @@ -import type { AccessRuleFetchError } from "../results/success/AccessRule"; - -export async function getAccessRules(): Promise { +export async function getAccessRules(): Promise { throw new Error("Not Implemented"); // const [publicAccess, agentAccess] = await Promise.all([ // universalAccess.getPublicAccess(uri, { fetch }), diff --git a/packages/solid/src/requester/requests/updateDataResource.ts b/packages/solid/src/requester/requests/updateDataResource.ts index e1ffc2a..d76ddbb 100644 --- a/packages/solid/src/requester/requests/updateDataResource.ts +++ b/packages/solid/src/requester/requests/updateDataResource.ts @@ -10,7 +10,7 @@ import type { LeafUri } from "../../util/uriTypes"; import { UnexpectedResourceError } from "../results/error/ErrorResult"; import type { HttpErrorResultType } from "../results/error/HttpErrorResult"; import { HttpErrorResult } from "../results/error/HttpErrorResult"; -import { UpdateSuccess } from "../results/success/UpdateSuccess"; +import type { UpdateSuccess } from "../results/success/UpdateSuccess"; import type { BasicRequestOptions } from "./requestOptions"; export type UpdateResult = UpdateSuccess | UpdateResultError; @@ -49,7 +49,11 @@ export async function updateDataResource( } return httpError; } - return new UpdateSuccess(uri); + return { + isError: false, + type: "updateSuccess", + uri, + }; } catch (err) { return UnexpectedResourceError.fromThrown(uri, err); } diff --git a/packages/solid/src/requester/requests/uploadResource.ts b/packages/solid/src/requester/requests/uploadResource.ts index b6d8b24..3ef39a3 100644 --- a/packages/solid/src/requester/requests/uploadResource.ts +++ b/packages/solid/src/requester/requests/uploadResource.ts @@ -7,7 +7,6 @@ import { import type { LeafUri } from "../../util/uriTypes"; import { UnexpectedResourceError } from "../results/error/ErrorResult"; import { HttpErrorResult } from "../results/error/HttpErrorResult"; -import { CreateSuccess } from "../results/success/CreateSuccess"; import type { LeafCreateAndOverwriteResult, LeafCreateIfAbsentResult, @@ -75,7 +74,12 @@ export async function uploadResource( if (options?.dataset) { addResourceRdfToContainer(uri, options.dataset); } - return new CreateSuccess(uri, !!overwrite); + return { + isError: false, + type: "createSuccess", + uri, + didOverwrite: !!overwrite, + }; } catch (err) { return UnexpectedResourceError.fromThrown(uri, err); } diff --git a/packages/solid/src/requester/results/error/HttpErrorResult.ts b/packages/solid/src/requester/results/error/HttpErrorResult.ts index f8bfebb..8475899 100644 --- a/packages/solid/src/requester/results/error/HttpErrorResult.ts +++ b/packages/solid/src/requester/results/error/HttpErrorResult.ts @@ -29,7 +29,11 @@ export abstract class HttpErrorResult extends ResourceError { } static isnt(response: Response) { - return response.status < 200 || response.status >= 300; + return ( + !(response.status >= 200 || response.status < 300) && + response.status !== 404 && + response.status !== 304 + ); } static checkResponse(uri: string, response: Response) { diff --git a/packages/solid/src/resource/Container.ts b/packages/solid/src/resource/Container.ts index a8b41d2..7758d7c 100644 --- a/packages/solid/src/resource/Container.ts +++ b/packages/solid/src/resource/Container.ts @@ -30,7 +30,7 @@ import type { ContainerUri, LeafUri } from "../util/uriTypes"; import type { Leaf } from "./Leaf"; import type { SharedStatuses } from "./Resource"; import { Resource } from "./Resource"; -import type { ResourceResult } from "./ResourceResult"; +import type { ResourceResult } from "./resourceResult/ResourceResult"; export class Container extends Resource { readonly uri: ContainerUri; @@ -290,7 +290,7 @@ export class Container extends Resource { ResourceResult > { const createResult = - (await this.handleCreateAndOverwrite()) as ContainerCreateIfAbsentResult; + (await this.handleCreateIfAbsent()) as ContainerCreateIfAbsentResult; if (createResult.isError) return createResult; return { ...createResult, resource: this }; } diff --git a/packages/solid/src/resource/Leaf.ts b/packages/solid/src/resource/Leaf.ts index ee5af4c..9ef5809 100644 --- a/packages/solid/src/resource/Leaf.ts +++ b/packages/solid/src/resource/Leaf.ts @@ -22,7 +22,7 @@ import type { LeafUri } from "../util/uriTypes"; import type { Container } from "./Container"; import type { SharedStatuses } from "./Resource"; import { Resource } from "./Resource"; -import type { ResourceResult } from "./ResourceResult"; +import type { ResourceResult } from "./resourceResult/ResourceResult"; export class Leaf extends Resource { readonly uri: LeafUri; @@ -199,7 +199,7 @@ export class Leaf extends Resource { ResourceResult > { const createResult = - (await this.handleCreateAndOverwrite()) as LeafCreateIfAbsentResult; + (await this.handleCreateIfAbsent()) as LeafCreateIfAbsentResult; if (createResult.isError) return createResult; return { ...createResult, resource: this }; } diff --git a/packages/solid/src/resource/Resource.ts b/packages/solid/src/resource/Resource.ts index 5c2aa20..394b9d2 100644 --- a/packages/solid/src/resource/Resource.ts +++ b/packages/solid/src/resource/Resource.ts @@ -176,7 +176,7 @@ export abstract class Resource extends (EventEmitter as new () => TypedEmitter<{ protected async handleCreateIfAbsent(): Promise< ContainerCreateIfAbsentResult | LeafCreateIfAbsentResult > { - const result = await this.requester.createDataResource(true); + const result = await this.requester.createDataResource(); this.status = result; if (result.isError) return result; this.updateWithCreateSuccess(result);