Fixed problem with objects being overwritten

main
Ailin Luca 2 years ago
parent fea6e3096a
commit 1ce4e46d50
  1. 16
      packages/demo-react/src/dashboard/BuildMainContainer.tsx
  2. 30
      packages/demo-react/src/dashboard/UploadButton.tsx
  3. 7
      packages/jsonld-dataset-proxy/src/util/addObjectToDataset.ts
  4. 14
      packages/jsonld-dataset-proxy/test/jsonldDatasetProxy.test.ts
  5. 5
      packages/solid-react/src/useLdoMethods.ts
  6. 1
      packages/solid/src/SolidLdoDataset.ts
  7. 1
      packages/solid/src/requester/requests/createDataResource.ts
  8. 4
      packages/solid/src/requester/requests/getAccessRules.ts
  9. 8
      packages/solid/src/requester/requests/updateDataResource.ts
  10. 8
      packages/solid/src/requester/requests/uploadResource.ts
  11. 6
      packages/solid/src/requester/results/error/HttpErrorResult.ts
  12. 4
      packages/solid/src/resource/Container.ts
  13. 4
      packages/solid/src/resource/Leaf.ts
  14. 2
      packages/solid/src/resource/Resource.ts

@ -18,18 +18,16 @@ export const BuildMainContainer: FunctionComponent<{
useEffect(() => { useEffect(() => {
if (session.webId) { if (session.webId) {
const webIdResource = getResource(session.webId as LeafUri); const webIdResource = getResource(session.webId as LeafUri);
webIdResource.getRootContainer().then(async (rootContainerResult) => { webIdResource.getRootContainer().then(async (rootContainer) => {
if (rootContainerResult.isError) { if (rootContainer.isError) {
alert(rootContainerResult.message); alert(rootContainer.message);
return; return;
} }
const mainContainer = getResource( const mainContainer = getResource(`${rootContainer.uri}demo-react/`);
`${rootContainerResult.rootContainer.uri}demo-react/`,
);
setMainContainer(mainContainer); setMainContainer(mainContainer);
await mainContainer.read(); const createResult = await mainContainer.createIfAbsent();
if (mainContainer.isAbsent()) { // Only set the access rules if the create was a success.
await mainContainer.createIfAbsent(); if (createResult.type === "createSuccess") {
await mainContainer.setAccessRules({ await mainContainer.setAccessRules({
public: { public: {
read: true, read: true,

@ -1,9 +1,10 @@
import React, { useCallback, useState, useRef } from "react"; import React, { useCallback, useState, useRef } from "react";
import type { FunctionComponent, FormEvent } 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 { v4 } from "uuid";
import { useLdo, useSolidAuth } from "@ldo/solid-react"; import { useLdo, useSolidAuth } from "@ldo/solid-react";
import { PostShShapeType } from "../.ldo/post.shapeTypes"; import { PostShShapeType } from "../.ldo/post.shapeTypes";
import { transactionChanges } from "@ldo/ldo";
export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({ export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({
mainContainer, mainContainer,
@ -18,28 +19,29 @@ export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({
e.preventDefault(); e.preventDefault();
// Create the container file // Create the container file
const mediaContainer = await mainContainer.createChildAndOverwrite( const mediaContainerResult = await mainContainer.createChildAndOverwrite(
`${v4()}/`, `${v4()}/`,
); );
if (mediaContainer.type === "error") { if (mediaContainerResult.isError) {
alert(mediaContainer.message); alert(mediaContainerResult.message);
return; return;
} }
const mediaContainer = mediaContainerResult.resource;
// Upload Image // Upload Image
let uploadedImage: Leaf | undefined; let uploadedImage: Leaf | undefined;
if (selectedFile) { if (selectedFile) {
const result = await mediaContainer.uploadChildAndOverwrite( const result = await mediaContainer.uploadChildAndOverwrite(
selectedFile.name, selectedFile.name as LeafUri,
selectedFile, selectedFile,
selectedFile.type, selectedFile.type,
); );
if (result.type === "error") { if (result.isError) {
alert(result.message); alert(result.message);
await mediaContainer.delete(); await mediaContainer.delete();
return; return;
} }
uploadedImage = result; uploadedImage = result.resource;
} }
// Create Post // Create Post
@ -49,6 +51,12 @@ export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({
indexResource.uri, indexResource.uri,
indexResource, 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; post.articleBody = message;
if (uploadedImage) { if (uploadedImage) {
post.image = { "@id": uploadedImage.uri }; post.image = { "@id": uploadedImage.uri };
@ -56,10 +64,16 @@ export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({
if (session.webId) { if (session.webId) {
post.publisher = { "@id": 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.type = { "@id": "SocialMediaPosting" };
post.uploadDate = new Date().toISOString(); post.uploadDate = new Date().toISOString();
const result = await commitData(post); const result = await commitData(post);
if (result.type === "error") { if (result.isError) {
alert(result.message); alert(result.message);
} }

@ -47,9 +47,10 @@ export function addRawValueToDatasetRecursive(
}); });
} else { } else {
// Delete any triples if the id is the same // Delete any triples if the id is the same
if (!visitedObjects.has(object) && !isSubjectProxy(value)) { // if (!visitedObjects.has(object) && !isSubjectProxy(value)) {
dataset.deleteMatches(object, undefined, undefined); // console.log("deleting 2", object.value);
} // dataset.deleteMatches(object, undefined, undefined);
// }
proxyContext.writeGraphs.forEach((graph) => { proxyContext.writeGraphs.forEach((graph) => {
dataset.add(quad(subject, predicate, object, graph)); dataset.add(quad(subject, predicate, object, graph));
}); });

@ -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(
'<http://example.com/Observation1> <http://hl7.org/fhir/subject> <http://example.com/Patient2> .\n<http://example.com/Patient1> <http://hl7.org/fhir/name> "Garrett" .\n<http://example.com/Patient1> <http://hl7.org/fhir/roommate> <http://example.com/Patient2> .\n<http://example.com/Patient2> <http://hl7.org/fhir/name> "Rob" .\n<http://example.com/Patient2> <http://hl7.org/fhir/roommate> <http://example.com/Patient1> .\n',
);
});
it("Does not remove the full object when it is replaced on an array", async () => { it("Does not remove the full object when it is replaced on an array", async () => {
const [dataset, observation] = await getTinyLoadedDataset(); const [dataset, observation] = await getTinyLoadedDataset();
const replacementPatient: PatientShape = { const replacementPatient: PatientShape = {
@ -643,7 +651,7 @@ describe("jsonldDatasetProxy", () => {
const [dataset, observation] = await getTinyLoadedDatasetWithBlankNodes(); const [dataset, observation] = await getTinyLoadedDatasetWithBlankNodes();
delete observation.subject?.roommate?.[0]; delete observation.subject?.roommate?.[0];
expect(dataset.toString()).toBe( expect(dataset.toString()).toBe(
'<http://example.com/Observation1> <http://hl7.org/fhir/subject> _:b25_Patient1 .\n_:b25_Patient1 <http://hl7.org/fhir/name> "Garrett" .\n', '<http://example.com/Observation1> <http://hl7.org/fhir/subject> _:b26_Patient1 .\n_:b26_Patient1 <http://hl7.org/fhir/name> "Garrett" .\n',
); );
}); });
@ -699,7 +707,7 @@ describe("jsonldDatasetProxy", () => {
expect(observation[Symbol.search]).toBe(undefined); 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 [dataset, observation] = await getTinyLoadedDataset();
const replacementPatient: PatientShape = { const replacementPatient: PatientShape = {
"@id": "http://example.com/Patient1", "@id": "http://example.com/Patient1",
@ -707,7 +715,7 @@ describe("jsonldDatasetProxy", () => {
}; };
observation.subject = replacementPatient; observation.subject = replacementPatient;
expect(dataset.toString()).toBe( expect(dataset.toString()).toBe(
'<http://example.com/Observation1> <http://hl7.org/fhir/subject> <http://example.com/Patient1> .\n<http://example.com/Patient1> <http://hl7.org/fhir/name> "Mister Sneaky" .\n<http://example.com/Patient2> <http://hl7.org/fhir/name> "Rob" .\n<http://example.com/Patient2> <http://hl7.org/fhir/roommate> <http://example.com/Patient1> .\n', '<http://example.com/Observation1> <http://hl7.org/fhir/subject> <http://example.com/Patient1> .\n<http://example.com/Patient1> <http://hl7.org/fhir/name> "Mister Sneaky" .\n<http://example.com/Patient1> <http://hl7.org/fhir/roommate> <http://example.com/Patient2> .\n<http://example.com/Patient2> <http://hl7.org/fhir/name> "Rob" .\n<http://example.com/Patient2> <http://hl7.org/fhir/roommate> <http://example.com/Patient1> .\n',
); );
}); });

@ -2,8 +2,9 @@ import type { LdoBase, ShapeType } from "@ldo/ldo";
import { transactionChanges } from "@ldo/ldo"; import { transactionChanges } from "@ldo/ldo";
import { write } from "@ldo/ldo"; import { write } from "@ldo/ldo";
import { startTransaction } 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 { Resource, SolidLdoDataset } from "@ldo/solid";
import type { Quad } from "@rdfjs/types";
export interface UseLdoMethods { export interface UseLdoMethods {
dataset: SolidLdoDataset; dataset: SolidLdoDataset;
@ -85,7 +86,7 @@ export function createUseLdoMethods(dataset: SolidLdoDataset): UseLdoMethods {
input: LdoBase, input: LdoBase,
): ReturnType<SolidLdoDataset["commitChangesToPod"]> { ): ReturnType<SolidLdoDataset["commitChangesToPod"]> {
const changes = transactionChanges(input); const changes = transactionChanges(input);
return dataset.commitChangesToPod(changes); return dataset.commitChangesToPod(changes as DatasetChanges<Quad>);
}, },
}; };
} }

@ -44,6 +44,7 @@ export class SolidLdoDataset extends LdoDataset {
| AggregateError<UpdateResultError | InvalidUriError> | AggregateError<UpdateResultError | InvalidUriError>
> { > {
const changesByGraph = splitChangesByGraph(changes); const changesByGraph = splitChangesByGraph(changes);
console.log(changesByGraph);
const results: [ const results: [
GraphNode, GraphNode,
DatasetChanges<Quad>, DatasetChanges<Quad>,

@ -109,6 +109,7 @@ export async function createDataResource(
} else { } else {
// Perform a read to check if it exists // Perform a read to check if it exists
const readResult = await readResource(uri, options); const readResult = await readResource(uri, options);
// If it does exist stop and return. // If it does exist stop and return.
if (readResult.type !== "absentReadSuccess") { if (readResult.type !== "absentReadSuccess") {
return readResult; return readResult;

@ -1,6 +1,4 @@
import type { AccessRuleFetchError } from "../results/success/AccessRule"; export async function getAccessRules(): Promise<undefined> {
export async function getAccessRules(): Promise<AccessRuleFetchError> {
throw new Error("Not Implemented"); throw new Error("Not Implemented");
// const [publicAccess, agentAccess] = await Promise.all([ // const [publicAccess, agentAccess] = await Promise.all([
// universalAccess.getPublicAccess(uri, { fetch }), // universalAccess.getPublicAccess(uri, { fetch }),

@ -10,7 +10,7 @@ import type { LeafUri } from "../../util/uriTypes";
import { UnexpectedResourceError } from "../results/error/ErrorResult"; import { UnexpectedResourceError } from "../results/error/ErrorResult";
import type { HttpErrorResultType } from "../results/error/HttpErrorResult"; import type { HttpErrorResultType } from "../results/error/HttpErrorResult";
import { HttpErrorResult } 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"; import type { BasicRequestOptions } from "./requestOptions";
export type UpdateResult = UpdateSuccess | UpdateResultError; export type UpdateResult = UpdateSuccess | UpdateResultError;
@ -49,7 +49,11 @@ export async function updateDataResource(
} }
return httpError; return httpError;
} }
return new UpdateSuccess(uri); return {
isError: false,
type: "updateSuccess",
uri,
};
} catch (err) { } catch (err) {
return UnexpectedResourceError.fromThrown(uri, err); return UnexpectedResourceError.fromThrown(uri, err);
} }

@ -7,7 +7,6 @@ import {
import type { LeafUri } from "../../util/uriTypes"; import type { LeafUri } from "../../util/uriTypes";
import { UnexpectedResourceError } from "../results/error/ErrorResult"; import { UnexpectedResourceError } from "../results/error/ErrorResult";
import { HttpErrorResult } from "../results/error/HttpErrorResult"; import { HttpErrorResult } from "../results/error/HttpErrorResult";
import { CreateSuccess } from "../results/success/CreateSuccess";
import type { import type {
LeafCreateAndOverwriteResult, LeafCreateAndOverwriteResult,
LeafCreateIfAbsentResult, LeafCreateIfAbsentResult,
@ -75,7 +74,12 @@ export async function uploadResource(
if (options?.dataset) { if (options?.dataset) {
addResourceRdfToContainer(uri, options.dataset); addResourceRdfToContainer(uri, options.dataset);
} }
return new CreateSuccess(uri, !!overwrite); return {
isError: false,
type: "createSuccess",
uri,
didOverwrite: !!overwrite,
};
} catch (err) { } catch (err) {
return UnexpectedResourceError.fromThrown(uri, err); return UnexpectedResourceError.fromThrown(uri, err);
} }

@ -29,7 +29,11 @@ export abstract class HttpErrorResult extends ResourceError {
} }
static isnt(response: Response) { 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) { static checkResponse(uri: string, response: Response) {

@ -30,7 +30,7 @@ import type { ContainerUri, LeafUri } from "../util/uriTypes";
import type { Leaf } from "./Leaf"; import type { Leaf } from "./Leaf";
import type { SharedStatuses } from "./Resource"; import type { SharedStatuses } from "./Resource";
import { Resource } from "./Resource"; import { Resource } from "./Resource";
import type { ResourceResult } from "./ResourceResult"; import type { ResourceResult } from "./resourceResult/ResourceResult";
export class Container extends Resource { export class Container extends Resource {
readonly uri: ContainerUri; readonly uri: ContainerUri;
@ -290,7 +290,7 @@ export class Container extends Resource {
ResourceResult<ContainerCreateIfAbsentResult, Container> ResourceResult<ContainerCreateIfAbsentResult, Container>
> { > {
const createResult = const createResult =
(await this.handleCreateAndOverwrite()) as ContainerCreateIfAbsentResult; (await this.handleCreateIfAbsent()) as ContainerCreateIfAbsentResult;
if (createResult.isError) return createResult; if (createResult.isError) return createResult;
return { ...createResult, resource: this }; return { ...createResult, resource: this };
} }

@ -22,7 +22,7 @@ import type { LeafUri } from "../util/uriTypes";
import type { Container } from "./Container"; import type { Container } from "./Container";
import type { SharedStatuses } from "./Resource"; import type { SharedStatuses } from "./Resource";
import { Resource } from "./Resource"; import { Resource } from "./Resource";
import type { ResourceResult } from "./ResourceResult"; import type { ResourceResult } from "./resourceResult/ResourceResult";
export class Leaf extends Resource { export class Leaf extends Resource {
readonly uri: LeafUri; readonly uri: LeafUri;
@ -199,7 +199,7 @@ export class Leaf extends Resource {
ResourceResult<LeafCreateIfAbsentResult, Leaf> ResourceResult<LeafCreateIfAbsentResult, Leaf>
> { > {
const createResult = const createResult =
(await this.handleCreateAndOverwrite()) as LeafCreateIfAbsentResult; (await this.handleCreateIfAbsent()) as LeafCreateIfAbsentResult;
if (createResult.isError) return createResult; if (createResult.isError) return createResult;
return { ...createResult, resource: this }; return { ...createResult, resource: this };
} }

@ -176,7 +176,7 @@ export abstract class Resource extends (EventEmitter as new () => TypedEmitter<{
protected async handleCreateIfAbsent(): Promise< protected async handleCreateIfAbsent(): Promise<
ContainerCreateIfAbsentResult | LeafCreateIfAbsentResult ContainerCreateIfAbsentResult | LeafCreateIfAbsentResult
> { > {
const result = await this.requester.createDataResource(true); const result = await this.requester.createDataResource();
this.status = result; this.status = result;
if (result.isError) return result; if (result.isError) return result;
this.updateWithCreateSuccess(result); this.updateWithCreateSuccess(result);

Loading…
Cancel
Save