Completed transaction on Solid Dataset

main
jaxoncreed 2 years ago
parent 64ea2070fd
commit a7f254fce9
  1. 28
      packages/solid/src/SolidLdoDataset.ts
  2. 2
      packages/solid/src/SolidLdoTransactionDataset.ts
  3. 93
      packages/solid/test/Integration.test.ts
  4. 4
      packages/solid/test/solidServer.helper.ts

@ -1,4 +1,5 @@
import { LdoDataset } from "@ldo/ldo";
import type { LdoBase, ShapeType } from "@ldo/ldo";
import { LdoDataset, startTransaction } from "@ldo/ldo";
import type { Dataset, DatasetFactory, Quad } from "@rdfjs/types";
import type { Container } from "./resource/Container";
import type { Leaf } from "./resource/Leaf";
@ -7,6 +8,8 @@ import type { SolidLdoDatasetContext } from "./SolidLdoDatasetContext";
import type { ContainerUri, LeafUri } from "./util/uriTypes";
import { SolidLdoTransactionDataset } from "./SolidLdoTransactionDataset";
import type { ITransactionDatasetFactory } from "@ldo/subscribable-dataset";
import type { SubjectNode } from "@ldo/rdf-utils";
import type { Resource } from "./resource/Resource";
/**
* A SolidLdoDataset has all the functionality of an LdoDataset with the added
@ -87,4 +90,27 @@ export class SolidLdoDataset extends LdoDataset {
this.transactionDatasetFactory,
);
}
/**
* Shorthand for solidLdoDataset
* .usingType(shapeType)
* .write(...resources.map((r) => r.uri))
* .fromSubject(subject);
* @param shapeType - The shapetype to represent the data
* @param subject - A subject URI
* @param resources - The resources changes to should written to
*/
createData<Type extends LdoBase>(
shapeType: ShapeType<Type>,
subject: string | SubjectNode,
resource: Resource,
...additionalResources: Resource[]
): Type {
const resources = [resource, ...additionalResources];
const linkedDataObject = this.usingType(shapeType)
.write(...resources.map((r) => r.uri))
.fromSubject(subject);
startTransaction(linkedDataObject);
return linkedDataObject;
}
}

@ -95,7 +95,7 @@ export class SolidLdoTransactionDataset
async ([graph, datasetChanges]) => {
if (graph.termType === "DefaultGraph") {
// Undefined means that this is the default graph
this.bulk(datasetChanges);
this.parentDataset.bulk(datasetChanges);
return [
graph,
datasetChanges,

@ -10,6 +10,7 @@ import type {
import { changeData, commitData, createSolidLdoDataset } from "../src";
import {
ROOT_CONTAINER,
WEB_ID,
createApp,
getAuthenticatedFetch,
} from "./solidServer.helper";
@ -20,7 +21,6 @@ import {
defaultGraph,
} from "@rdfjs/data-model";
import type { CreateSuccess } from "../src/requester/results/success/CreateSuccess";
import { createDataset } from "@ldo/dataset";
import type { AggregateSuccess } from "../src/requester/results/success/SuccessResult";
import type {
UpdateDefaultGraphSuccess,
@ -89,6 +89,12 @@ const TEST_CONTAINER_TTL = `@prefix dc: <http://purl.org/dc/terms/>.
posix:size 522.
<sample.txt> posix:mtime 1697810234;
posix:size 10.`;
const TEST_CONTAINER_ACL_URI = `${TEST_CONTAINER_URI}.acl`;
const TEST_CONTAINER_ACL = `<#b30e3fd1-b5a8-4763-ad9d-e95de9cf7933> a <http://www.w3.org/ns/auth/acl#Authorization>;
<http://www.w3.org/ns/auth/acl#accessTo> <${TEST_CONTAINER_URI}>;
<http://www.w3.org/ns/auth/acl#default> <${TEST_CONTAINER_URI}>;
<http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Read>, <http://www.w3.org/ns/auth/acl#Write>, <http://www.w3.org/ns/auth/acl#Append>, <http://www.w3.org/ns/auth/acl#Control>;
<http://www.w3.org/ns/auth/acl#agent> <${WEB_ID}>.`;
async function testRequestLoads<ReturnVal>(
request: () => Promise<ReturnVal>,
@ -164,6 +170,13 @@ describe("Integration", () => {
slug: TEST_CONTAINER_SLUG,
},
});
await authFetch(TEST_CONTAINER_ACL_URI, {
method: "PUT",
headers: {
"content-type": "text/turtle",
},
body: TEST_CONTAINER_ACL,
});
await Promise.all([
authFetch(TEST_CONTAINER_URI, {
method: "POST",
@ -328,8 +341,8 @@ describe("Integration", () => {
expect(result.isError).toBe(true);
if (!result.isError) return;
expect(result.type).toBe("noncompliantPodError");
expect(result.message).toBe(
"Response from https://solidweb.me/jackson3/test_ldo/sample2.ttl is not compliant with the Solid Specification: Resource requests must return a content-type header.",
expect(result.message).toMatch(
/\Response from .* is not compliant with the Solid Specification: Resource requests must return a content-type header\./,
);
});
@ -349,8 +362,8 @@ describe("Integration", () => {
expect(result.isError).toBe(true);
if (!result.isError) return;
expect(result.type).toBe("noncompliantPodError");
expect(result.message).toBe(
'Response from https://solidweb.me/jackson3/test_ldo/sample2.ttl is not compliant with the Solid Specification: Request returned noncompliant turtle: Unexpected "Error" on line 1.',
expect(result.message).toMatch(
/\Response from .* is not compliant with the Solid Specification: Request returned noncompliant turtle: Unexpected "Error" on line 1\./,
);
});
@ -384,8 +397,8 @@ describe("Integration", () => {
expect(result.isError).toBe(true);
if (!result.isError) return;
expect(result.type).toBe("noncompliantPodError");
expect(result.message).toBe(
"Response from https://solidweb.me/jackson3/test_ldo/ is not compliant with the Solid Specification: No link header present in request.",
expect(result.message).toMatch(
/\Response from .* is not compliant with the Solid Specification: No link header present in request\./,
);
});
@ -404,26 +417,22 @@ describe("Integration", () => {
resource.read(),
]);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(result.type).toBe("dataReadSuccess");
expect(result1.type).toBe("dataReadSuccess");
expect(fetchMock).toHaveBeenCalledTimes(1);
});
it("batches the read request when a read request is in queue", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const [, result, result1] = await Promise.all([
resource.update({
added: createDataset([
createQuad(namedNode("a"), namedNode("b"), namedNode("c")),
]),
}),
resource.createAndOverwrite(),
resource.read(),
resource.read(),
]);
expect(fetchMock).toHaveBeenCalledTimes(3);
expect(result.type).toBe("dataReadSuccess");
expect(result1.type).toBe("dataReadSuccess");
expect(fetchMock).toHaveBeenCalledTimes(2);
});
});
@ -544,8 +553,8 @@ describe("Integration", () => {
expect(result.isError).toBe(true);
if (!result.isError) return;
expect(result.type).toBe("noncompliantPodError");
expect(result.message).toBe(
"Response from https://solidweb.me/jackson3/test_ldo/ is not compliant with the Solid Specification: No link header present in request.",
expect(result.message).toMatch(
/\Response from .* is not compliant with the Solid Specification: No link header present in request\./,
);
});
@ -1419,16 +1428,55 @@ describe("Integration", () => {
* ===========================================================================
*/
describe("methods", () => {
it("uses changeData to start a transaction", () => {
const resource = solidLdoDataset.getResource(
"https://example.com/resource.ttl",
it("creates a data object for a specific subject", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const post = solidLdoDataset.createData(
PostShShapeType,
"https://example.com/subject",
resource,
);
post.type = { "@id": "CreativeWork" };
expect(post.type["@id"]).toBe("CreativeWork");
const result = await commitData(post);
expect(result.type).toBe("aggregateSuccess");
expect(
solidLdoDataset.has(
createQuad(
namedNode("https://example.com/subject"),
namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
namedNode("http://schema.org/CreativeWork"),
namedNode(SAMPLE_DATA_URI),
),
),
).toBe(true);
});
it("handles an error when committing data", async () => {
fetchMock.mockResolvedValueOnce(
new Response(SAMPLE_DATA_URI, {
status: 500,
}),
);
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const post = solidLdoDataset.createData(
PostShShapeType,
"https://example.com/subject",
resource,
);
post.type = { "@id": "CreativeWork" };
expect(post.type["@id"]).toBe("CreativeWork");
const result = await commitData(post);
expect(result.isError).toBe(true);
});
it("uses changeData to start a transaction", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
solidLdoDataset.add(
createQuad(
namedNode("https://example.com/subject"),
namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
namedNode("http://schema.org/CreativeWork"),
namedNode("https://example.com/resource.ttl"),
namedNode(SAMPLE_DATA_URI),
),
);
const post = solidLdoDataset
@ -1437,14 +1485,15 @@ describe("Integration", () => {
const cPost = changeData(post, resource);
cPost.type = { "@id": "SocialMediaPosting" };
expect(cPost.type["@id"]).toBe("SocialMediaPosting");
commitData(cPost);
const result = await commitData(cPost);
expect(result.isError).toBe(false);
expect(
solidLdoDataset.has(
createQuad(
namedNode("https://example.com/subject"),
namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
namedNode("http://schema.org/SocialMediaPosting"),
namedNode("https://example.com/resource.ttl"),
namedNode(SAMPLE_DATA_URI),
),
),
).toBe(true);

@ -21,8 +21,10 @@ const config = [
];
export const SERVER_DOMAIN = process.env.SERVER || "http://localhost:3001/";
export const ROOT_ROUTE = process.env.ROOT_CONTAINER || "example/";
export const ROOT_ROUTE = process.env.ROOT_CONTAINER || "";
export const ROOT_CONTAINER = `${SERVER_DOMAIN}${ROOT_ROUTE}`;
export const WEB_ID =
process.env.WEB_ID || `${SERVER_DOMAIN}example/profile/card#me`;
// Use an increased timeout, since the CSS server takes too much setup time.
jest.setTimeout(40_000);

Loading…
Cancel
Save