Typescript errors in tests fixed

main
Jackson Morgan 6 months ago
parent 81aa841f6b
commit a2d03e5431
  1. 62
      packages/connected-solid/src/getStorageFromWebId.ts
  2. 1
      packages/connected-solid/src/index.ts
  3. 6
      packages/connected-solid/src/resources/SolidContainer.ts
  4. 4
      packages/connected-solid/src/resources/SolidResource.ts
  5. 64
      packages/connected-solid/test/ErrorResult.test.ts
  6. 215
      packages/connected-solid/test/Integration.test.ts
  7. 4
      packages/connected-solid/test/solidServer.helper.ts
  8. 28
      packages/connected/src/ConnectedLdoDataset.ts
  9. 2
      packages/connected/src/ConnectedLdoTransactionDataset.ts
  10. 7
      packages/connected/src/ConnectedPlugin.ts
  11. 1
      packages/connected/src/index.ts
  12. 91
      packages/connected/src/methods.ts
  13. 2
      packages/connected/src/results/success/SuccessResult.ts

@ -0,0 +1,62 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { ConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected";
import type { SolidContainerUri, SolidLeafUri } from "./types";
import type { GetStorageContainerFromWebIdSuccess } from "./requester/results/success/CheckRootContainerSuccess";
import type { CheckRootResultError } from "./requester/requests/checkRootContainer";
import type { ReadResultError } from "./requester/requests/readResource";
import type { NoRootContainerError } from "./requester/results/error/NoRootContainerError";
import type { SolidLeaf } from "./resources/SolidLeaf";
import type { SolidContainer } from "./resources/SolidContainer";
import type { SolidConnectedPlugin } from "./SolidConnectedPlugin";
import { ProfileWithStorageShapeType } from "./.ldo/solid.shapeTypes";
/**
* Gets a list of root storage containers for a user given their WebId
* @param webId: The webId for the user
* @returns A list of storages if successful, an error if not
* @example
* ```typescript
* const result = await getStorageFromWebId(
* solidLdoDataset,
* "https://example.com/profile/card#me"
* );
* if (result.isError) {
* // Do something
* }
* console.log(result.storageContainer[0].uri);
* ```
*/
export async function getStorageFromWebId(
webId: SolidLeafUri,
dataset: ConnectedLdoDataset<(SolidConnectedPlugin | ConnectedPlugin)[]>,
): Promise<
| GetStorageContainerFromWebIdSuccess
| CheckRootResultError
| ReadResultError<SolidLeaf | SolidContainer>
| NoRootContainerError<SolidContainer>
> {
const webIdResource = dataset.getResource(webId) as SolidLeaf;
const readResult = await webIdResource.readIfUnfetched();
if (readResult.isError) return readResult;
const profile = this.usingType(ProfileWithStorageShapeType).fromSubject(
webId,
);
if (profile.storage && profile.storage.size > 0) {
const containers = profile.storage.map((storageNode) =>
this.getResource(storageNode["@id"] as SolidContainerUri),
);
return {
type: "getStorageContainerFromWebIdSuccess",
isError: false,
storageContainers: containers,
};
}
const getContainerResult = await webIdResource.getRootContainer();
if (getContainerResult.type === "container")
return {
type: "getStorageContainerFromWebIdSuccess",
isError: false,
storageContainers: [getContainerResult],
};
return getContainerResult;
}

@ -1,6 +1,7 @@
export * from "./types";
export * from "./SolidConnectedPlugin";
export * from "./createSolidLdoDataset";
export * from "./getStorageFromWebId";
export * from "./resources/SolidResource";
export * from "./resources/SolidContainer";

@ -29,7 +29,7 @@ import type {
SolidContainerUri,
SolidLeafSlug,
} from "../types";
import type { AbsentReadSuccess } from "@ldo/connected";
import type { AbsentReadSuccess, ReadSuccess } from "@ldo/connected";
import { AggregateSuccess, IgnoredInvalidUpdateSuccess } from "@ldo/connected";
import {
Unfetched,
@ -135,11 +135,11 @@ export class SolidContainer extends SolidResource {
* @param result - the result of the read success
*/
protected updateWithReadSuccess(
result: ContainerReadSuccess | AbsentReadSuccess<this>,
result: ReadSuccess<this> | ContainerReadSuccess,
): void {
super.updateWithReadSuccess(result);
if (result.type === "containerReadSuccess") {
this.rootContainer = result.isRootContainer;
this.rootContainer = (result as ContainerReadSuccess).isRootContainer;
}
}

@ -383,7 +383,7 @@ export abstract class SolidResource
* A helper method updates this resource's internal state upon read success
* @param result - the result of the read success
*/
protected updateWithReadSuccess(result: ReadSuccess<this>) {
protected updateWithReadSuccess(result: ReadSuccess<Resource>) {
this.absent = result.type === "absentReadSuccess";
this.didInitialFetch = true;
}
@ -469,7 +469,7 @@ export abstract class SolidResource
* A helper method updates this resource's internal state upon create success
* @param _result - the result of the create success
*/
protected updateWithCreateSuccess(result: ResourceSuccess<this>) {
protected updateWithCreateSuccess(result: ResourceSuccess<Resource>) {
this.absent = false;
this.didInitialFetch = true;
if (isReadSuccess(result)) {

@ -1,64 +0,0 @@
import {
AggregateError,
ErrorResult,
ResourceError,
UnexpectedResourceError,
} from "../src/requester/results/error/ErrorResult";
import { InvalidUriError } from "../src/requester/results/error/InvalidUriError";
describe("ErrorResult", () => {
describe("fromThrown", () => {
it("returns an UnexpecteResourceError if a string is provided", () => {
expect(
UnexpectedResourceError.fromThrown("https://example.com/", "hello")
.message,
).toBe("hello");
});
it("returns an UnexpecteResourceError if an odd valud is provided", () => {
expect(
UnexpectedResourceError.fromThrown("https://example.com/", 5).message,
).toBe("Error of type number thrown: 5");
});
});
describe("AggregateError", () => {
it("flattens aggregate errors provided to the constructor", () => {
const err1 = UnexpectedResourceError.fromThrown("https://abc.com", "1");
const err2 = UnexpectedResourceError.fromThrown("https://abc.com", "2");
const err3 = UnexpectedResourceError.fromThrown("https://abc.com", "3");
const err4 = UnexpectedResourceError.fromThrown("https://abc.com", "4");
const aggErr1 = new AggregateError([err1, err2]);
const aggErr2 = new AggregateError([err3, err4]);
const finalAgg = new AggregateError([aggErr1, aggErr2]);
expect(finalAgg.errors.length).toBe(4);
});
});
describe("default messages", () => {
class ConcreteResourceError extends ResourceError {
readonly type = "concreteResourceError" as const;
}
class ConcreteErrorResult extends ErrorResult {
readonly type = "concreteErrorResult" as const;
}
it("ResourceError fallsback to a default message if none is provided", () => {
expect(new ConcreteResourceError("https://example.com/").message).toBe(
"An unkown error for https://example.com/",
);
});
it("ErrorResult fallsback to a default message if none is provided", () => {
expect(new ConcreteErrorResult().message).toBe(
"An unkown error was encountered.",
);
});
it("InvalidUriError fallsback to a default message if none is provided", () => {
expect(new InvalidUriError("https://example.com/").message).toBe(
"https://example.com/ is an invalid uri.",
);
});
});
});

@ -7,12 +7,6 @@ import {
defaultGraph,
} from "@rdfjs/data-model";
import type { CreateSuccess } from "../src/requester/results/success/CreateSuccess";
import type {
IgnoredInvalidUpdateSuccess,
UpdateDefaultGraphSuccess,
UpdateSuccess,
} from "../src/requester/results/success/UpdateSuccess";
import type { InvalidUriError } from "../src/requester/results/error/InvalidUriError";
import { Buffer } from "buffer";
import { PostShShapeType } from "./.ldo/post.shapeTypes";
import type {
@ -26,13 +20,26 @@ import { generateAuthFetch } from "./authFetch.helper";
import { wait } from "./utils.helper";
import fs from "fs/promises";
import path from "path";
import type {
SolidContainer,
SolidContainerUri,
SolidLeaf,
SolidLeafUri,
import type { GetWacRuleSuccess, UpdateResultError, WacRule } from "../src";
import {
createSolidLdoDataset,
type SolidConnectedPlugin,
type SolidContainer,
type SolidContainerUri,
type SolidLeaf,
type SolidLeafUri,
} from "../src";
import { ConnectedLdoDataset } from "@ldo/connected";
import type {
AggregateError,
AggregateSuccess,
IgnoredInvalidUpdateSuccess,
InvalidUriError,
UnexpectedResourceError,
UpdateDefaultGraphSuccess,
UpdateSuccess,
} from "@ldo/connected";
import { changeData, commitData, ConnectedLdoDataset } from "@ldo/connected";
import { getStorageFromWebId } from "../src/getStorageFromWebId";
const TEST_CONTAINER_SLUG = "test_ldo/";
const TEST_CONTAINER_URI =
@ -143,7 +150,7 @@ describe("Integration", () => {
Promise<Response>,
[input: RequestInfo | URL, init?: RequestInit | undefined]
>;
let solidLdoDataset: SolidLdoDataset;
let solidLdoDataset: ConnectedLdoDataset<SolidConnectedPlugin[]>;
let previousJestId: string | undefined;
let previousNodeEnv: string | undefined;
@ -170,7 +177,8 @@ describe("Integration", () => {
beforeEach(async () => {
fetchMock = jest.fn(authFetch);
solidLdoDataset = createSolidLdoDataset({ fetch: fetchMock });
solidLdoDataset = createSolidLdoDataset();
solidLdoDataset.setContext("solid", { fetch: fetchMock });
// Create a new document called sample.ttl
await authFetch(ROOT_CONTAINER, {
method: "POST",
@ -267,27 +275,28 @@ describe("Integration", () => {
expect(resource.isPresent()).toBe(true);
});
it("Auto reads a resource", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI, {
autoLoad: true,
});
// Wait until the resource is auto-loaded
await new Promise<void>((resolve) => {
const interval = setInterval(() => {
if (!resource.isReading()) {
clearInterval(interval);
resolve();
}
}, 250);
});
expect(
solidLdoDataset.match(
namedNode("http://example.org/#spiderman"),
namedNode("http://www.perceive.net/schemas/relationship/enemyOf"),
namedNode("http://example.org/#green-goblin"),
).size,
).toBe(1);
});
// TODO: Possibly re-enable if Auto-read is required, but it might not be
// it("Auto reads a resource", async () => {
// const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI, {
// autoLoad: true,
// });
// // Wait until the resource is auto-loaded
// await new Promise<void>((resolve) => {
// const interval = setInterval(() => {
// if (!resource.isReading()) {
// clearInterval(interval);
// resolve();
// }
// }, 250);
// });
// expect(
// solidLdoDataset.match(
// namedNode("http://example.org/#spiderman"),
// namedNode("http://www.perceive.net/schemas/relationship/enemyOf"),
// namedNode("http://example.org/#green-goblin"),
// ).size,
// ).toBe(1);
// });
it("Reads a container", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
@ -671,7 +680,10 @@ describe("Integration", () => {
*/
describe("getStorageFromWebId", () => {
it("Gets storage when a pim:storage field isn't present", async () => {
const result = await solidLdoDataset.getStorageFromWebId(SAMPLE_DATA_URI);
const result = await getStorageFromWebId(
SAMPLE_DATA_URI,
solidLdoDataset,
);
expect(result.type).toBe("getStorageContainerFromWebIdSuccess");
const realResult = result as GetStorageContainerFromWebIdSuccess;
expect(realResult.storageContainers.length).toBe(1);
@ -679,8 +691,10 @@ describe("Integration", () => {
});
it("Gets storage when a pim:storage field is present", async () => {
const result =
await solidLdoDataset.getStorageFromWebId(SAMPLE_PROFILE_URI);
const result = await getStorageFromWebId(
SAMPLE_PROFILE_URI,
solidLdoDataset,
);
expect(result.type).toBe("getStorageContainerFromWebIdSuccess");
const realResult = result as GetStorageContainerFromWebIdSuccess;
expect(realResult.storageContainers.length).toBe(2);
@ -694,14 +708,20 @@ describe("Integration", () => {
it("Passes any errors returned from the read method", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const result = await solidLdoDataset.getStorageFromWebId(SAMPLE_DATA_URI);
const result = await getStorageFromWebId(
SAMPLE_DATA_URI,
solidLdoDataset,
);
expect(result.isError).toBe(true);
});
it("Passes any errors returned from the getRootContainer method", async () => {
fetchMock.mockResolvedValueOnce(new Response(""));
fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const result = await solidLdoDataset.getStorageFromWebId(SAMPLE_DATA_URI);
const result = await getStorageFromWebId(
SAMPLE_DATA_URI,
solidLdoDataset,
);
expect(result.isError).toBe(true);
});
});
@ -725,7 +745,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.didOverwrite).toBe(false);
expect(
solidLdoDataset.has(
@ -754,7 +774,7 @@ describe("Integration", () => {
},
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.didOverwrite).toBe(true);
expect(
solidLdoDataset.has(
@ -783,7 +803,7 @@ describe("Integration", () => {
},
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidContainer>;
expect(createSuccess.didOverwrite).toBe(false);
expect(
solidLdoDataset.has(
@ -896,7 +916,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.didOverwrite).toBe(false);
expect(
solidLdoDataset.has(
@ -954,7 +974,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidContainer>;
expect(createSuccess.didOverwrite).toBe(false);
expect(
solidLdoDataset.has(
@ -1047,11 +1067,11 @@ describe("Integration", () => {
expect(result.isError).toBe(true);
expect(result.type).toBe("aggregateError");
const aggregateError = result as AggregateError<
| ServerHttpError
| UnexpectedHttpError
| UnauthenticatedHttpError
| UnexpectedResourceError
| NoncompliantPodError
| ServerHttpError<SolidLeaf | SolidContainer>
| UnexpectedHttpError<SolidLeaf | SolidContainer>
| UnauthenticatedHttpError<SolidLeaf | SolidContainer>
| UnexpectedResourceError<SolidLeaf | SolidContainer>
| NoncompliantPodError<SolidLeaf | SolidContainer>
>;
expect(aggregateError.errors[0].type).toBe("serverError");
});
@ -1070,11 +1090,11 @@ describe("Integration", () => {
expect(result.isError).toBe(true);
expect(result.type).toBe("aggregateError");
const aggregateError = result as AggregateError<
| ServerHttpError
| UnexpectedHttpError
| UnauthenticatedHttpError
| UnexpectedResourceError
| NoncompliantPodError
| ServerHttpError<SolidLeaf | SolidContainer>
| UnexpectedHttpError<SolidLeaf | SolidContainer>
| UnauthenticatedHttpError<SolidLeaf | SolidContainer>
| UnexpectedResourceError<SolidLeaf | SolidContainer>
| NoncompliantPodError<SolidLeaf | SolidContainer>
>;
expect(aggregateError.errors[0].type).toBe("serverError");
});
@ -1119,7 +1139,7 @@ describe("Integration", () => {
const transaction = solidLdoDataset.startTransaction();
transaction.add(normanQuad);
transaction.delete(goblinQuad);
return transaction.commitToPod();
return transaction.commitToRemote();
},
solidLdoDataset.getResource(SAMPLE_DATA_URI),
{
@ -1129,7 +1149,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("aggregateSuccess");
const aggregateSuccess = result as AggregateSuccess<
ResourceSuccess<UpdateSuccess, Leaf>
UpdateSuccess<SolidLeaf>
>;
expect(aggregateSuccess.results.length).toBe(1);
expect(aggregateSuccess.results[0].type === "updateSuccess").toBe(true);
@ -1142,7 +1162,7 @@ describe("Integration", () => {
() => {
const transaction = solidLdoDataset.startTransaction();
transaction.delete(goblinQuad);
return transaction.commitToPod();
return transaction.commitToRemote();
},
solidLdoDataset.getResource(SAMPLE_DATA_URI),
{
@ -1152,7 +1172,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("aggregateSuccess");
const aggregateSuccess = result as AggregateSuccess<
ResourceSuccess<UpdateSuccess, Leaf>
UpdateSuccess<SolidLeaf>
>;
expect(aggregateSuccess.results.length).toBe(1);
expect(aggregateSuccess.results[0].type === "updateSuccess").toBe(true);
@ -1165,12 +1185,13 @@ describe("Integration", () => {
const transaction = solidLdoDataset.startTransaction();
transaction.add(normanQuad);
transaction.delete(goblinQuad);
const result = await transaction.commitToPod();
const result = await transaction.commitToRemote();
expect(result.isError).toBe(true);
expect(result.type).toBe("aggregateError");
const aggregateError = result as AggregateError<
UpdateResultError | InvalidUriError
| UpdateResultError<SolidLeaf | SolidContainer>
| InvalidUriError<SolidLeaf | SolidContainer>
>;
expect(aggregateError.errors.length).toBe(1);
expect(aggregateError.errors[0].type).toBe("serverError");
@ -1183,11 +1204,12 @@ describe("Integration", () => {
const transaction = solidLdoDataset.startTransaction();
transaction.add(normanQuad);
transaction.delete(goblinQuad);
const result = await transaction.commitToPod();
const result = await transaction.commitToRemote();
expect(result.isError).toBe(true);
expect(result.type).toBe("aggregateError");
const aggregateError = result as AggregateError<
UpdateResultError | InvalidUriError
| UpdateResultError<SolidLeaf | SolidContainer>
| InvalidUriError<SolidLeaf | SolidContainer>
>;
expect(aggregateError.errors.length).toBe(1);
expect(aggregateError.errors[0].type).toBe("unexpectedResourceError");
@ -1202,11 +1224,12 @@ describe("Integration", () => {
);
const transaction = solidLdoDataset.startTransaction();
transaction.add(badContainerQuad);
const result = await transaction.commitToPod();
const result = await transaction.commitToRemote();
expect(result.isError).toBe(false);
expect(result.type).toBe("aggregateSuccess");
const aggregateSuccess = result as AggregateSuccess<
UpdateSuccess | IgnoredInvalidUpdateSuccess
| UpdateSuccess<SolidLeaf | SolidContainer>
| IgnoredInvalidUpdateSuccess<SolidLeaf | SolidContainer>
>;
expect(aggregateSuccess.results.length).toBe(1);
expect(aggregateSuccess.results[0].type).toBe(
@ -1223,10 +1246,10 @@ describe("Integration", () => {
);
const transaction = solidLdoDataset.startTransaction();
transaction.add(defaultGraphQuad);
const result = await transaction.commitToPod();
const result = await transaction.commitToRemote();
expect(result.type).toBe("aggregateSuccess");
const aggregateSuccess = result as AggregateSuccess<
ResourceSuccess<UpdateSuccess | UpdateDefaultGraphSuccess, Leaf>
UpdateSuccess<SolidLeaf | SolidContainer> | UpdateDefaultGraphSuccess
>;
expect(aggregateSuccess.results.length).toBe(1);
expect(aggregateSuccess.results[0].type).toBe(
@ -1254,8 +1277,8 @@ describe("Integration", () => {
const [, updateResult1, updateResult2] = await Promise.all([
resource.read(),
transaction1.commitToPod(),
transaction2.commitToPod(),
transaction1.commitToRemote(),
transaction2.commitToRemote(),
]);
expect(updateResult1.type).toBe("aggregateSuccess");
expect(updateResult2.type).toBe("aggregateSuccess");
@ -1312,7 +1335,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.didOverwrite).toBe(false);
expect(
solidLdoDataset.has(
@ -1348,7 +1371,7 @@ describe("Integration", () => {
},
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.didOverwrite).toBe(true);
expect(
solidLdoDataset.has(
@ -1474,7 +1497,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as CreateSuccess;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.didOverwrite).toBe(false);
expect(
solidLdoDataset.has(
@ -1634,7 +1657,7 @@ describe("Integration", () => {
const result = await resource.createChildAndOverwrite(SAMPLE2_DATA_SLUG);
expect(result.type).toBe("createSuccess");
const createSuccess = result as ResourceResult<CreateSuccess, Leaf>;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.resource.uri).toBe(SAMPLE2_DATA_URI);
expect(createSuccess.didOverwrite).toBe(false);
expect(
@ -1657,7 +1680,7 @@ describe("Integration", () => {
const result = await resource.createChildIfAbsent(SAMPLE2_DATA_SLUG);
expect(result.type).toBe("createSuccess");
const createSuccess = result as ResourceResult<CreateSuccess, Leaf>;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.resource.uri).toBe(SAMPLE2_DATA_URI);
expect(createSuccess.didOverwrite).toBe(false);
expect(
@ -1684,7 +1707,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as ResourceResult<CreateSuccess, Leaf>;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.resource.uri).toBe(SAMPLE2_BINARY_URI);
expect(createSuccess.didOverwrite).toBe(false);
expect(
@ -1711,7 +1734,7 @@ describe("Integration", () => {
);
expect(result.type).toBe("createSuccess");
const createSuccess = result as ResourceResult<CreateSuccess, Leaf>;
const createSuccess = result as CreateSuccess<SolidLeaf>;
expect(createSuccess.resource.uri).toBe(SAMPLE2_BINARY_URI);
expect(createSuccess.didOverwrite).toBe(false);
expect(
@ -1740,7 +1763,9 @@ describe("Integration", () => {
const container = solidLdoDataset.getResource(TEST_CONTAINER_URI);
const wacResult = await container.getWac();
expect(wacResult.isError).toBe(false);
const wacSuccess = wacResult as GetWacRuleSuccess;
const wacSuccess = wacResult as GetWacRuleSuccess<
SolidLeaf | SolidContainer
>;
expect(wacSuccess.wacRule.public).toEqual({
read: true,
write: true,
@ -1765,7 +1790,9 @@ describe("Integration", () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(false);
const wacSuccess = wacResult as GetWacRuleSuccess;
const wacSuccess = wacResult as GetWacRuleSuccess<
SolidLeaf | SolidContainer
>;
expect(wacSuccess.wacRule.public).toEqual({
read: true,
write: true,
@ -1791,7 +1818,9 @@ describe("Integration", () => {
await resource.getWac();
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(false);
const wacSuccess = wacResult as GetWacRuleSuccess;
const wacSuccess = wacResult as GetWacRuleSuccess<
SolidLeaf | SolidContainer
>;
expect(wacSuccess.wacRule.public).toEqual({
read: true,
write: true,
@ -1837,7 +1866,9 @@ describe("Integration", () => {
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
expect(
(wacResult as NoncompliantPodError<SolidLeaf | SolidContainer>).message,
).toBe(
`Response from ${SAMPLE_DATA_URI} is not compliant with the Solid Specification: No link header present in request.`,
);
});
@ -1853,7 +1884,9 @@ describe("Integration", () => {
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
expect(
(wacResult as NoncompliantPodError<SolidLeaf | SolidContainer>).message,
).toBe(
`Response from ${SAMPLE_DATA_URI} is not compliant with the Solid Specification: There must be one link with a rel="acl"`,
);
});
@ -1898,7 +1931,9 @@ describe("Integration", () => {
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
expect(
(wacResult as NoncompliantPodError<SolidLeaf | SolidContainer>).message,
).toBe(
`Response from card.acl is not compliant with the Solid Specification: Request returned noncompliant turtle: Unexpected "BAD" on line 1.\nBAD TURTLE`,
);
});
@ -1930,7 +1965,9 @@ describe("Integration", () => {
const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError");
expect((wacResult as NoncompliantPodError).message).toBe(
expect(
(wacResult as NoncompliantPodError<SolidLeaf | SolidContainer>).message,
).toBe(
`Response from ${ROOT_CONTAINER} is not compliant with the Solid Specification: Resource "${ROOT_CONTAINER}" has no Effective ACL resource`,
);
});
@ -1958,7 +1995,9 @@ describe("Integration", () => {
const readResult = await resource.getWac({ ignoreCache: true });
expect(readResult.isError).toBe(false);
expect(readResult.type).toBe("getWacRuleSuccess");
const rules = (readResult as GetWacRuleSuccess).wacRule;
const rules = (
readResult as GetWacRuleSuccess<SolidLeaf | SolidContainer>
).wacRule;
expect(rules).toEqual(newRules);
});
@ -1970,7 +2009,9 @@ describe("Integration", () => {
const readResult = await resource.getWac({ ignoreCache: true });
expect(readResult.isError).toBe(false);
expect(readResult.type).toBe("getWacRuleSuccess");
const rules = (readResult as GetWacRuleSuccess).wacRule;
const rules = (
readResult as GetWacRuleSuccess<SolidLeaf | SolidContainer>
).wacRule;
expect(rules).toEqual(newRules);
});
@ -1986,7 +2027,9 @@ describe("Integration", () => {
const readResult = await resource.getWac({ ignoreCache: true });
expect(readResult.isError).toBe(false);
expect(readResult.type).toBe("getWacRuleSuccess");
const rules = (readResult as GetWacRuleSuccess).wacRule;
const rules = (
readResult as GetWacRuleSuccess<SolidLeaf | SolidContainer>
).wacRule;
expect(rules).toEqual(moreRules);
});

@ -4,10 +4,12 @@ import * as path from "path";
import type { App } from "@solid/community-server";
import { AppRunner, resolveModulePath } from "@solid/community-server";
import "jest-rdf";
import type { SolidContainerUri } from "../src";
export const SERVER_DOMAIN = process.env.SERVER || "http://localhost:3001/";
export const ROOT_ROUTE = process.env.ROOT_CONTAINER || "";
export const ROOT_CONTAINER = `${SERVER_DOMAIN}${ROOT_ROUTE}`;
export const ROOT_CONTAINER =
`${SERVER_DOMAIN}${ROOT_ROUTE}` as SolidContainerUri;
export const WEB_ID =
process.env.WEB_ID || `${SERVER_DOMAIN}example/profile/card#me`;

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { LdoDataset } from "@ldo/ldo";
import type { LdoBase, ShapeType } from "@ldo/ldo";
import { LdoDataset, startTransaction } from "@ldo/ldo";
import type { ConnectedPlugin } from "./ConnectedPlugin";
import type { Dataset, DatasetFactory, Quad } from "@rdfjs/types";
import type { ITransactionDatasetFactory } from "@ldo/subscribable-dataset";
@ -10,6 +11,8 @@ import type {
ReturnTypeFromArgs,
} from "./IConnectedLdoDataset";
import { ConnectedLdoTransactionDataset } from "./ConnectedLdoTransactionDataset";
import type { SubjectNode } from "@ldo/rdf-utils";
import type { Resource } from "./Resource";
export class ConnectedLdoDataset<
Plugins extends ConnectedPlugin<any, any, any, any>[],
@ -114,6 +117,29 @@ export class ConnectedLdoDataset<
return newResourceResult as any;
}
/**
* 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;
}
setContext<
Name extends Plugins[number]["name"],
Plugin extends Extract<Plugins[number], { name: Name }>,

@ -137,7 +137,7 @@ export class ConnectedLdoTransactionDataset<Plugins extends ConnectedPlugin[]>
);
}
async commitChanges(): Promise<
async commitToRemote(): Promise<
| AggregateSuccess<
| Extract<
Awaited<ReturnType<Plugins[number]["types"]["resource"]["update"]>>,

@ -10,12 +10,9 @@ export interface ConnectedPlugin<
ContextType = any,
> {
name: Name;
getResource(
uri: UriType,
context: ConnectedContext<ConnectedPlugin<any, any, any, any>[]>,
): ResourceType;
getResource(uri: UriType, context: ConnectedContext<any[]>): ResourceType;
createResource(
context: ConnectedContext<ConnectedPlugin<any, any, any, any>[]>,
context: ConnectedContext<any[]>,
): Promise<ResourceType | ErrorResult>;
isUriValid(uri: string): uri is UriType;
normalizeUri?: (uri: UriType) => UriType;

@ -5,6 +5,7 @@ export * from "./ConnectedPlugin";
export * from "./Resource";
export * from "./InvalidIdentifierResource";
export * from "./ConnectedContext";
export * from "./methods";
export * from "./util/splitChangesByGraph";

@ -0,0 +1,91 @@
import { startTransaction, type LdoBase, write, getDataset } from "@ldo/ldo";
import type { Quad } from "@rdfjs/types";
import { _proxyContext, getProxyFromObject } from "@ldo/jsonld-dataset-proxy";
import type { SubscribableDataset } from "@ldo/subscribable-dataset";
import type { Resource } from "./Resource";
import type { ConnectedLdoTransactionDataset } from "./ConnectedLdoTransactionDataset";
import type {
AggregateSuccess,
SuccessResult,
} from "./results/success/SuccessResult";
import type { AggregateError, ErrorResult } from "./results/error/ErrorResult";
/**
* Begins tracking changes to eventually commit.
*
* @param input - A linked data object to track changes on
* @param resource - A resource that all additions will eventually be committed to
* @param additionalResources - Any additional resources that changes will eventually be committed to
*
* @returns A transactable Linked Data Object
*
* @example
* ```typescript
* import { changeData } from "@ldo/solid";
*
* // ...
*
* const profile = solidLdoDataset
* .using(ProfileShapeType)
* .fromSubject("https://example.com/profile#me");
* const resource = solidLdoDataset.getResource("https://example.com/profile");
*
* const cProfile = changeData(profile, resource);
* cProfile.name = "My New Name";
* const result = await commitData(cProfile);
* ```
*/
export function changeData<Type extends LdoBase>(
input: Type,
resource: Resource,
...additionalResources: Resource[]
): Type {
const resources = [resource, ...additionalResources];
// Clone the input and set a graph
const [transactionLdo] = write(...resources.map((r) => r.uri)).usingCopy(
input,
);
// Start a transaction with the input
startTransaction(transactionLdo);
// Return
return transactionLdo;
}
/**
* Commits the transaction to the global dataset, syncing all subscribing
* components and Solid Pods
*
* @param input - A transactable linked data object
*
* @example
* ```typescript
* import { changeData } from "@ldo/solid";
*
* // ...
*
* const profile = solidLdoDataset
* .using(ProfileShapeType)
* .fromSubject("https://example.com/profile#me");
* const resource = solidLdoDataset.getResource("https://example.com/profile");
*
* const cProfile = changeData(profile, resource);
* cProfile.name = "My New Name";
* const result = await commitData(cProfile);
* ```
*/
export async function commitData(
input: LdoBase,
): Promise<AggregateSuccess<SuccessResult> | AggregateError<ErrorResult>> {
const transactionDataset = getDataset(
input,
) as ConnectedLdoTransactionDataset<[]>;
const result = await transactionDataset.commitToRemote();
if (result.isError) return result;
// Take the LdoProxy out of commit mode. This uses hidden methods of JSONLD-DATASET-PROXY
const proxy = getProxyFromObject(input);
proxy[_proxyContext] = proxy[_proxyContext].duplicate({
dataset: proxy[_proxyContext].state
.parentDataset as SubscribableDataset<Quad>,
});
return result;
}

@ -22,7 +22,7 @@ export abstract class ResourceSuccess<
/**
* The resource that was successful
*/
resource: Resource;
resource: ResourceType;
constructor(resource: ResourceType) {
super();

Loading…
Cancel
Save