getStorageFromWebId

main
Jackson Morgan 1 year ago
parent a13851f29f
commit f78409af97
  1. 8
      packages/solid/src/.ldo/solid.context.ts
  2. 21
      packages/solid/src/.ldo/solid.schema.ts
  3. 13
      packages/solid/src/.ldo/solid.shapeTypes.ts
  4. 16
      packages/solid/src/.ldo/solid.typings.ts
  5. 7
      packages/solid/src/.shapes/solid.shex
  6. 53
      packages/solid/src/SolidLdoDataset.ts
  7. 8
      packages/solid/src/requester/results/success/CheckRootContainerSuccess.ts
  8. 61
      packages/solid/test/Integration.test.ts

@ -1,4 +1,4 @@
import type { ContextDefinition } from "jsonld";
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
@ -15,7 +15,6 @@ export const solidContext: ContextDefinition = {
modified: {
"@id": "http://purl.org/dc/terms/modified",
"@type": "http://www.w3.org/2001/XMLSchema#string",
"@container": "@set",
},
contains: {
"@id": "http://www.w3.org/ns/ldp#contains",
@ -26,11 +25,14 @@ export const solidContext: ContextDefinition = {
mtime: {
"@id": "http://www.w3.org/ns/posix/stat#mtime",
"@type": "http://www.w3.org/2001/XMLSchema#decimal",
"@container": "@set",
},
size: {
"@id": "http://www.w3.org/ns/posix/stat#size",
"@type": "http://www.w3.org/2001/XMLSchema#integer",
},
storage: {
"@id": "http://www.w3.org/ns/pim/space#storage",
"@type": "@id",
"@container": "@set",
},
};

@ -1,4 +1,4 @@
import type { Schema } from "shexj";
import { Schema } from "shexj";
/**
* =============================================================================
@ -210,5 +210,24 @@ export const solidSchema: Schema = {
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
{
id: "http://www.w3.org/ns/lddps#ProfileWithStorage",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
id: "http://www.w3.org/ns/lddps#ProfileWithStorageShape",
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/pim/space#storage",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: -1,
},
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
],
};

@ -1,7 +1,7 @@
import type { ShapeType } from "@ldo/ldo";
import { ShapeType } from "@ldo/ldo";
import { solidSchema } from "./solid.schema";
import { solidContext } from "./solid.context";
import type { Container, Resource } from "./solid.typings";
import { Container, Resource, ProfileWithStorage } from "./solid.typings";
/**
* =============================================================================
@ -26,3 +26,12 @@ export const ResourceShapeType: ShapeType<Resource> = {
shape: "http://www.w3.org/ns/lddps#Resource",
context: solidContext,
};
/**
* ProfileWithStorage ShapeType
*/
export const ProfileWithStorageShapeType: ShapeType<ProfileWithStorage> = {
schema: solidSchema,
shape: "http://www.w3.org/ns/lddps#ProfileWithStorage",
context: solidContext,
};

@ -1,4 +1,4 @@
import type { ContextDefinition } from "jsonld";
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
@ -57,9 +57,6 @@ export interface Resource {
| {
"@id": "Resource2";
}
| {
"@id": "Container";
}
)[];
/**
* Date modified
@ -74,3 +71,14 @@ export interface Resource {
*/
size?: number;
}
/**
* ProfileWithStorage Type
*/
export interface ProfileWithStorage {
"@id"?: string;
"@context"?: ContextDefinition;
storage?: {
"@id": string;
}[];
}

@ -6,6 +6,7 @@ PREFIX ldps: <http://www.w3.org/ns/lddps#>
PREFIX dct: <http://purl.org/dc/terms/>
PREFIX stat: <http://www.w3.org/ns/posix/stat#>
PREFIX tur: <http://www.w3.org/ns/iana/media-types/text/turtle#>
PREFIX pim: <http://www.w3.org/ns/pim/space#>
ldps:Container EXTRA a {
$ldps:ContainerShape (
@ -34,3 +35,9 @@ ldps:Resource EXTRA a {
// rdfs:comment "size of this container";
)
}
ldps:ProfileWithStorage EXTRA a {
$ldps:ProfileWithStorageShape (
pim:storage IRI *;
)
}

@ -10,6 +10,11 @@ import { SolidLdoTransactionDataset } from "./SolidLdoTransactionDataset";
import type { ITransactionDatasetFactory } from "@ldo/subscribable-dataset";
import type { SubjectNode } from "@ldo/rdf-utils";
import type { Resource } from "./resource/Resource";
import type { CheckRootResultError } from "./requester/requests/checkRootContainer";
import type { NoRootContainerError } from "./requester/results/error/NoRootContainerError";
import type { ReadResultError } from "./requester/requests/readResource";
import { ProfileWithStorageShapeType } from "./.ldo/solid.shapeTypes";
import type { GetStorageContainerFromWebIdSuccess } from "./requester/results/success/CheckRootContainerSuccess";
/**
* A SolidLdoDataset has all the functionality of an LdoDataset with the added
@ -113,4 +118,52 @@ export class SolidLdoDataset extends LdoDataset {
startTransaction(linkedDataObject);
return linkedDataObject;
}
/**
* 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 solidLdoDataset
* .getStorageFromWebId("https://example.com/profile/card#me");
* if (result.isError) {
* // Do something
* }
* console.log(result.storageContainer[0].uri);
* ```
*/
async getStorageFromWebId(
webId: LeafUri,
): Promise<
| GetStorageContainerFromWebIdSuccess
| CheckRootResultError
| ReadResultError
| NoRootContainerError
> {
const webIdResource = this.getResource(webId);
const readResult = await webIdResource.readIfUnfetched();
if (readResult.isError) return readResult;
const profile = this.usingType(ProfileWithStorageShapeType).fromSubject(
webId,
);
if (profile.storage && profile.storage.length > 0) {
const containers = profile.storage.map((storageNode) =>
this.getResource(storageNode["@id"] as ContainerUri),
);
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,4 +1,5 @@
import type { ResourceSuccess } from "./SuccessResult";
import type { Container } from "../../../resource/Container";
import type { ResourceSuccess, SuccessResult } from "./SuccessResult";
/**
* Indicates that the request to check if a resource is the root container was
@ -11,3 +12,8 @@ export interface CheckRootContainerSuccess extends ResourceSuccess {
*/
isRootContainer: boolean;
}
export interface GetStorageContainerFromWebIdSuccess extends SuccessResult {
type: "getStorageContainerFromWebIdSuccess";
storageContainers: Container[];
}

@ -45,6 +45,7 @@ import type {
import type { NoncompliantPodError } from "../src/requester/results/error/NoncompliantPodError";
import type { GetWacRuleSuccess } from "../src/resource/wac/results/GetWacRuleSuccess";
import type { WacRule } from "../src/resource/wac/WacRule";
import type { GetStorageContainerFromWebIdSuccess } from "../src/requester/results/success/CheckRootContainerSuccess";
const TEST_CONTAINER_SLUG = "test_ldo/";
const TEST_CONTAINER_URI =
@ -58,6 +59,7 @@ const SAMPLE2_BINARY_URI =
`${TEST_CONTAINER_URI}${SAMPLE2_BINARY_SLUG}` as LeafUri;
const SAMPLE_CONTAINER_URI =
`${TEST_CONTAINER_URI}sample_container/` as ContainerUri;
const SAMPLE_PROFILE_URI = `${TEST_CONTAINER_URI}profile.ttl` as LeafUri;
const SPIDER_MAN_TTL = `@base <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@ -98,6 +100,11 @@ const TEST_CONTAINER_ACL = `<#b30e3fd1-b5a8-4763-ad9d-e95de9cf7933> a <http://ww
<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}>;
<http://www.w3.org/ns/auth/acl#agentClass> <http://xmlns.com/foaf/0.1/Agent>, <http://www.w3.org/ns/auth/acl#AuthenticatedAgent>.`;
const SAMPLE_PROFILE_TTL = `
@prefix pim: <http://www.w3.org/ns/pim/space#> .
<${SAMPLE_PROFILE_URI}> pim:storage <https://example.com/A/>, <https://example.com/B/> .
`;
async function testRequestLoads<ReturnVal>(
request: () => Promise<ReturnVal>,
@ -191,6 +198,11 @@ describe("Integration", () => {
headers: { "content-type": "text/plain", slug: "sample.txt" },
body: "some text.",
}),
authFetch(TEST_CONTAINER_URI, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "profile.ttl" },
body: SAMPLE_PROFILE_TTL,
}),
]);
});
@ -208,6 +220,9 @@ describe("Integration", () => {
authFetch(SAMPLE2_BINARY_URI, {
method: "DELETE",
}),
authFetch(SAMPLE_PROFILE_URI, {
method: "DELETE",
}),
authFetch(SAMPLE_CONTAINER_URI, {
method: "DELETE",
}),
@ -283,7 +298,7 @@ describe("Integration", () => {
isDoingInitialFetch: true,
});
expect(result.type).toBe("containerReadSuccess");
expect(resource.children().length).toBe(2);
expect(resource.children().length).toBe(3);
});
it("Reads a binary leaf", async () => {
@ -497,7 +512,7 @@ describe("Integration", () => {
},
);
expect(result.type).toBe("containerReadSuccess");
expect(resource.children().length).toBe(2);
expect(resource.children().length).toBe(3);
});
it("reads an unfetched leaf", async () => {
@ -528,7 +543,7 @@ describe("Integration", () => {
const result = await resource.readIfUnfetched();
expect(fetchMock).not.toHaveBeenCalled();
expect(result.type).toBe("containerReadSuccess");
expect(resource.children().length).toBe(2);
expect(resource.children().length).toBe(3);
});
it("returns a cached existing data leaf", async () => {
@ -652,6 +667,46 @@ describe("Integration", () => {
});
});
/**
* Get Storage From WebId
*/
describe("getStorageFromWebId", () => {
it("Gets storage when a pim:storage field isn't present", async () => {
const result = await solidLdoDataset.getStorageFromWebId(SAMPLE_DATA_URI);
expect(result.type).toBe("getStorageContainerFromWebIdSuccess");
const realResult = result as GetStorageContainerFromWebIdSuccess;
expect(realResult.storageContainers.length).toBe(1);
expect(realResult.storageContainers[0].uri).toBe(ROOT_CONTAINER);
});
it("Gets storage when a pim:storage field is present", async () => {
const result =
await solidLdoDataset.getStorageFromWebId(SAMPLE_PROFILE_URI);
expect(result.type).toBe("getStorageContainerFromWebIdSuccess");
const realResult = result as GetStorageContainerFromWebIdSuccess;
expect(realResult.storageContainers.length).toBe(2);
expect(realResult.storageContainers[0].uri).toBe(
"https://example.com/A/",
);
expect(realResult.storageContainers[0].uri).toBe(
"https://example.com/B/",
);
});
it("Passes any errors returned from the read method", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const result = await solidLdoDataset.getStorageFromWebId(SAMPLE_DATA_URI);
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);
expect(result.isError).toBe(true);
});
});
/**
* ===========================================================================
* Create

Loading…
Cancel
Save