From fabd0c0c5808f0f205cc2ea23475f96681aaefeb Mon Sep 17 00:00:00 2001 From: Jackson Morgan Date: Sat, 3 May 2025 15:10:44 -0400 Subject: [PATCH] Setup tests for Link Traversal --- package-lock.json | 4 +- .../src/SolidConnectedPlugin.ts | 7 + packages/connected/package.json | 6 +- packages/connected/src/ConnectedLdoDataset.ts | 14 + .../src/ConnectedLdoTransactionDataset.ts | 8 + .../src/types/IConnectedLdoDataset.ts | 27 + packages/connected/src/types/ILinkQuery.ts | 49 +- .../test/.ldo/solidProfile.context.ts | 459 +++++++++++ .../test/.ldo/solidProfile.schema.ts | 749 ++++++++++++++++++ .../test/.ldo/solidProfile.shapeTypes.ts | 71 ++ .../test/.ldo/solidProfile.typings.ts | 293 +++++++ .../connected/test/.shapes/solidProfile.shex | 121 +++ packages/connected/test/LinkTraversalData.ts | 42 + .../test/LinkTraversalIntegration.test.ts | 132 +-- packages/connected/test/authFetch.helper.ts | 112 --- .../server-config-without-websocket.json | 44 - .../connected/test/configs/server-config.json | 43 - .../test/configs/solid-css-seed.json | 9 - packages/connected/test/setup-tests.ts | 3 - packages/connected/test/solidServer.helper.ts | 42 - packages/test-solid-server/Readme.md | 16 +- packages/test-solid-server/src/authFetch.ts | 2 +- 22 files changed, 1888 insertions(+), 365 deletions(-) create mode 100644 packages/connected/test/.ldo/solidProfile.context.ts create mode 100644 packages/connected/test/.ldo/solidProfile.schema.ts create mode 100644 packages/connected/test/.ldo/solidProfile.shapeTypes.ts create mode 100644 packages/connected/test/.ldo/solidProfile.typings.ts create mode 100644 packages/connected/test/.shapes/solidProfile.shex create mode 100644 packages/connected/test/LinkTraversalData.ts delete mode 100644 packages/connected/test/authFetch.helper.ts delete mode 100644 packages/connected/test/configs/server-config-without-websocket.json delete mode 100644 packages/connected/test/configs/server-config.json delete mode 100644 packages/connected/test/configs/solid-css-seed.json delete mode 100644 packages/connected/test/setup-tests.ts delete mode 100644 packages/connected/test/solidServer.helper.ts diff --git a/package-lock.json b/package-lock.json index 763d88c..308c357 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25931,6 +25931,7 @@ }, "devDependencies": { "@ldo/connected-solid": "^1.0.0-alpha.3", + "@ldo/test-solid-server": "^1.0.0-alpha.8", "@rdfjs/data-model": "^1.2.0", "@rdfjs/types": "^1.0.1", "cross-env": "^7.0.3", @@ -25989,6 +25990,7 @@ "devDependencies": { "@inrupt/solid-client-authn-core": "^2.2.6", "@ldo/cli": "^1.0.0-alpha.3", + "@ldo/test-solid-server": "^1.0.0-alpha.8", "@rdfjs/data-model": "^1.2.0", "@rdfjs/types": "^1.0.1", "@solid-notifications/types": "^0.1.2", @@ -26332,7 +26334,7 @@ }, "packages/test-solid-server": { "name": "@ldo/test-solid-server", - "version": "1.0.0-alpha.3", + "version": "1.0.0-alpha.8", "license": "MIT", "dependencies": { "@inrupt/solid-client-authn-core": "^2.2.6", diff --git a/packages/connected-solid/src/SolidConnectedPlugin.ts b/packages/connected-solid/src/SolidConnectedPlugin.ts index 7e19817..632df97 100644 --- a/packages/connected-solid/src/SolidConnectedPlugin.ts +++ b/packages/connected-solid/src/SolidConnectedPlugin.ts @@ -80,4 +80,11 @@ export const solidConnectedPlugin: SolidConnectedPlugin = { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore "Types" only exists for the typing system types: {}, + + normalizeUri(uri: SolidUri): SolidUri { + const url = new URL(uri); + url.hash = ""; + url.search = ""; + return url.toString() as SolidUri; + }, }; diff --git a/packages/connected/package.json b/packages/connected/package.json index 08d82af..bcc75e0 100644 --- a/packages/connected/package.json +++ b/packages/connected/package.json @@ -6,11 +6,12 @@ "scripts": { "build": "tsc --project tsconfig.build.json", "watch": "tsc --watch", - "test": "jest --coverage", + "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage", "test:watch": "jest --watch", "prepublishOnly": "npm run test && npm run build", "lint": "eslint src/** --fix --no-error-on-unmatched-pattern", - "docs": "typedoc --plugin typedoc-plugin-markdown" + "docs": "typedoc --plugin typedoc-plugin-markdown", + "build:ldo": "ldo build --input test/.shapes --output test/.ldo" }, "repository": { "type": "git", @@ -24,6 +25,7 @@ "homepage": "https://github.com/o-development/ldobjects/tree/main/packages/solid#readme", "devDependencies": { "@ldo/connected-solid": "^1.0.0-alpha.3", + "@ldo/test-solid-server": "^1.0.0-alpha.8", "@rdfjs/data-model": "^1.2.0", "@rdfjs/types": "^1.0.1", "cross-env": "^7.0.3", diff --git a/packages/connected/src/ConnectedLdoDataset.ts b/packages/connected/src/ConnectedLdoDataset.ts index f71f424..2bcae86 100644 --- a/packages/connected/src/ConnectedLdoDataset.ts +++ b/packages/connected/src/ConnectedLdoDataset.ts @@ -153,6 +153,10 @@ export class ConnectedLdoDataset< if (!plugin) return new InvalidIdentifierResource(uri) as any; const normalizedUri = plugin.normalizeUri?.(uri) ?? uri; + console.log("plugin", plugin); + console.log("func", plugin.normalizeUri); + console.log(normalizedUri); + let resource = this.resourceMap.get(normalizedUri); if (!resource) { resource = plugin.getResource(uri, this.context); @@ -162,6 +166,16 @@ export class ConnectedLdoDataset< return resource as any; } + getResources(): GetResourceReturnType[] { + console.log("IM IN HERE"); + console.log(this.resourceMap); + return Array.from(this.resourceMap.values()); + } + + getFetchedResources(): GetResourceReturnType[] { + return this.getResources().filter((resource) => resource.isFetched()); + } + /** * Generates a random uri and creates a resource. * diff --git a/packages/connected/src/ConnectedLdoTransactionDataset.ts b/packages/connected/src/ConnectedLdoTransactionDataset.ts index 2c3acf1..34339b6 100644 --- a/packages/connected/src/ConnectedLdoTransactionDataset.ts +++ b/packages/connected/src/ConnectedLdoTransactionDataset.ts @@ -101,6 +101,14 @@ export class ConnectedLdoTransactionDataset return this.context.dataset.getResource(uri, pluginName); } + getResources(): Plugins[number]["types"]["resource"][] { + return this.context.dataset.getResources(); + } + + getFetchedResources(): Plugins[number]["types"]["resource"][] { + return this.context.dataset.getFetchedResources(); + } + createResource< Name extends Plugins[number]["name"], Plugin extends Extract, diff --git a/packages/connected/src/types/IConnectedLdoDataset.ts b/packages/connected/src/types/IConnectedLdoDataset.ts index 214e4f7..5724058 100644 --- a/packages/connected/src/types/IConnectedLdoDataset.ts +++ b/packages/connected/src/types/IConnectedLdoDataset.ts @@ -79,6 +79,33 @@ export interface IConnectedLdoDataset pluginName?: Name, ): GetResourceReturnType; + /** + * Retireves a representation of all Resources referenced by this dataset + * This does not necessarily mean that it's been fetched (use the + * `getFetchedResources` method for that). It simply means that at one point + * it was referenced. + * + * @returns a Resource array + * + * @example + * ```typescript + * const allResources = connectedLdoDataset.getResources(); + * ``` + */ + getResources(): Plugins[number]["types"]["resource"][]; + + /** + * Retireves a representation of all Resources that have been fetched. + * + * @returns a Resource array + * + * @example + * ```typescript + * const allResources = connectedLdoDataset.getFetchedResources(); + * ``` + */ + getFetchedResources(): Plugins[number]["types"]["resource"][]; + /** * Generates a random uri and creates a resource. * diff --git a/packages/connected/src/types/ILinkQuery.ts b/packages/connected/src/types/ILinkQuery.ts index d62f6b2..ec4970e 100644 --- a/packages/connected/src/types/ILinkQuery.ts +++ b/packages/connected/src/types/ILinkQuery.ts @@ -3,8 +3,9 @@ // If I ever want to implement a global query interface, this is a good place // to start. -import type { LdoBase, LdSet, ShapeType } from "@ldo/ldo"; -import { ProfileShapeType } from "packages/ldo/test/profileData"; +import type { LdoBase, LdSet } from "@ldo/ldo"; +// import { SolidProfileShapeShapeType } from "../../test/.ldo/solidProfile.shapeTypes"; +// import type { SolidProfileShape } from "../../test/.ldo/solidProfile.typings"; /** * Link Query Input @@ -12,7 +13,7 @@ import { ProfileShapeType } from "packages/ldo/test/profileData"; export type LQInput = LQInputObject; export type LQInputObject = Partial<{ - [key in keyof Type]: LQInputFlattenSet; + [key in Exclude]: LQInputFlattenSet; }>; export type LQInputSubSet = Type extends object @@ -45,7 +46,10 @@ export type LQReturn> = LQReturnObject< >; export type LQReturnObject> = { - [key in keyof Required as undefined extends Input[key] + [key in Exclude< + keyof Required, + "@context" + > as undefined extends Input[key] ? never : key]: Input[key] extends LQInputFlattenSet ? undefined extends Type[key] @@ -56,7 +60,9 @@ export type LQReturnObject> = { export type LQReturnSubSet = Input extends LQInputSubSet ? Input extends LQInputObject - ? LQReturnObject + ? Input extends true + ? Type + : LQReturnObject : Type : never; @@ -89,20 +95,31 @@ export interface ILinkQuery> { fromSubject(): ExpandDeep>; } -// TODO: Remove test functions // function test>( -// _shapeType: ShapeType, -// _input: Input, +// shapeType: ShapeType, +// input: Input, // ): ExpandDeep> { -// throw new Error("Not Implemeneted"); +// throw new Error("Not Implemented"); // } -// const result = test(ProfileShapeType, { -// fn: true, + +// type TestLQInput = { +// name: true; +// knows: { +// name: true; +// }; +// }; + +// type testReturn = LQReturn; + +// type test2 = LQReturnSubSet; + +// type lqInputObject = LQInputObject; + +// type meh = TestLQInput extends true ? true : false; + +// const thing = test(SolidProfileShapeShapeType, { // name: true, -// hasTelephone: { -// type: { -// "@id": true, -// }, -// value: true, +// knows: { +// name: true, // }, // }); diff --git a/packages/connected/test/.ldo/solidProfile.context.ts b/packages/connected/test/.ldo/solidProfile.context.ts new file mode 100644 index 0000000..9fdffc3 --- /dev/null +++ b/packages/connected/test/.ldo/solidProfile.context.ts @@ -0,0 +1,459 @@ +import { LdoJsonldContext } from "@ldo/ldo"; + +/** + * ============================================================================= + * solidProfileContext: JSONLD Context for solidProfile + * ============================================================================= + */ +export const solidProfileContext: LdoJsonldContext = { + type: { + "@id": "@type", + }, + Person: { + "@id": "http://schema.org/Person", + "@context": { + type: { + "@id": "@type", + }, + fn: { + "@id": "http://www.w3.org/2006/vcard/ns#fn", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + hasAddress: { + "@id": "http://www.w3.org/2006/vcard/ns#hasAddress", + "@type": "@id", + "@isCollection": true, + }, + hasEmail: { + "@id": "http://www.w3.org/2006/vcard/ns#hasEmail", + "@type": "@id", + "@isCollection": true, + }, + hasPhoto: { + "@id": "http://www.w3.org/2006/vcard/ns#hasPhoto", + "@type": "@id", + }, + img: { + "@id": "http://xmlns.com/foaf/0.1/img", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + hasTelephone: { + "@id": "http://www.w3.org/2006/vcard/ns#hasTelephone", + "@type": "@id", + "@isCollection": true, + }, + phone: { + "@id": "http://www.w3.org/2006/vcard/ns#phone", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + organizationName: { + "@id": "http://www.w3.org/2006/vcard/ns#organization-name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + role: { + "@id": "http://www.w3.org/2006/vcard/ns#role", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + trustedApp: { + "@id": "http://www.w3.org/ns/auth/acl#trustedApp", + "@type": "@id", + "@isCollection": true, + }, + key: { + "@id": "http://www.w3.org/ns/auth/cert#key", + "@type": "@id", + "@isCollection": true, + }, + inbox: { + "@id": "http://www.w3.org/ns/ldp#inbox", + "@type": "@id", + }, + preferencesFile: { + "@id": "http://www.w3.org/ns/pim/space#preferencesFile", + "@type": "@id", + }, + storage: { + "@id": "http://www.w3.org/ns/pim/space#storage", + "@type": "@id", + "@isCollection": true, + }, + account: { + "@id": "http://www.w3.org/ns/solid/terms#account", + "@type": "@id", + }, + privateTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#privateTypeIndex", + "@type": "@id", + "@isCollection": true, + }, + publicTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#publicTypeIndex", + "@type": "@id", + "@isCollection": true, + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@isCollection": true, + }, + }, + }, + Person2: { + "@id": "http://xmlns.com/foaf/0.1/Person", + "@context": { + type: { + "@id": "@type", + }, + fn: { + "@id": "http://www.w3.org/2006/vcard/ns#fn", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + hasAddress: { + "@id": "http://www.w3.org/2006/vcard/ns#hasAddress", + "@type": "@id", + "@isCollection": true, + }, + hasEmail: { + "@id": "http://www.w3.org/2006/vcard/ns#hasEmail", + "@type": "@id", + "@isCollection": true, + }, + hasPhoto: { + "@id": "http://www.w3.org/2006/vcard/ns#hasPhoto", + "@type": "@id", + }, + img: { + "@id": "http://xmlns.com/foaf/0.1/img", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + hasTelephone: { + "@id": "http://www.w3.org/2006/vcard/ns#hasTelephone", + "@type": "@id", + "@isCollection": true, + }, + phone: { + "@id": "http://www.w3.org/2006/vcard/ns#phone", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + organizationName: { + "@id": "http://www.w3.org/2006/vcard/ns#organization-name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + role: { + "@id": "http://www.w3.org/2006/vcard/ns#role", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + trustedApp: { + "@id": "http://www.w3.org/ns/auth/acl#trustedApp", + "@type": "@id", + "@isCollection": true, + }, + key: { + "@id": "http://www.w3.org/ns/auth/cert#key", + "@type": "@id", + "@isCollection": true, + }, + inbox: { + "@id": "http://www.w3.org/ns/ldp#inbox", + "@type": "@id", + }, + preferencesFile: { + "@id": "http://www.w3.org/ns/pim/space#preferencesFile", + "@type": "@id", + }, + storage: { + "@id": "http://www.w3.org/ns/pim/space#storage", + "@type": "@id", + "@isCollection": true, + }, + account: { + "@id": "http://www.w3.org/ns/solid/terms#account", + "@type": "@id", + }, + privateTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#privateTypeIndex", + "@type": "@id", + "@isCollection": true, + }, + publicTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#publicTypeIndex", + "@type": "@id", + "@isCollection": true, + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@isCollection": true, + }, + }, + }, + fn: { + "@id": "http://www.w3.org/2006/vcard/ns#fn", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + hasAddress: { + "@id": "http://www.w3.org/2006/vcard/ns#hasAddress", + "@type": "@id", + "@isCollection": true, + }, + countryName: { + "@id": "http://www.w3.org/2006/vcard/ns#country-name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + locality: { + "@id": "http://www.w3.org/2006/vcard/ns#locality", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + postalCode: { + "@id": "http://www.w3.org/2006/vcard/ns#postal-code", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + region: { + "@id": "http://www.w3.org/2006/vcard/ns#region", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + streetAddress: { + "@id": "http://www.w3.org/2006/vcard/ns#street-address", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + hasEmail: { + "@id": "http://www.w3.org/2006/vcard/ns#hasEmail", + "@type": "@id", + "@isCollection": true, + }, + Dom: { + "@id": "http://www.w3.org/2006/vcard/ns#Dom", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Home: { + "@id": "http://www.w3.org/2006/vcard/ns#Home", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + ISDN: { + "@id": "http://www.w3.org/2006/vcard/ns#ISDN", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Internet: { + "@id": "http://www.w3.org/2006/vcard/ns#Internet", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Intl: { + "@id": "http://www.w3.org/2006/vcard/ns#Intl", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Label: { + "@id": "http://www.w3.org/2006/vcard/ns#Label", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Parcel: { + "@id": "http://www.w3.org/2006/vcard/ns#Parcel", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Postal: { + "@id": "http://www.w3.org/2006/vcard/ns#Postal", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Pref: { + "@id": "http://www.w3.org/2006/vcard/ns#Pref", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + Work: { + "@id": "http://www.w3.org/2006/vcard/ns#Work", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + X400: { + "@id": "http://www.w3.org/2006/vcard/ns#X400", + "@context": { + type: { + "@id": "@type", + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + }, + }, + value: { + "@id": "http://www.w3.org/2006/vcard/ns#value", + "@type": "@id", + }, + hasPhoto: { + "@id": "http://www.w3.org/2006/vcard/ns#hasPhoto", + "@type": "@id", + }, + img: { + "@id": "http://xmlns.com/foaf/0.1/img", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + hasTelephone: { + "@id": "http://www.w3.org/2006/vcard/ns#hasTelephone", + "@type": "@id", + "@isCollection": true, + }, + phone: { + "@id": "http://www.w3.org/2006/vcard/ns#phone", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + organizationName: { + "@id": "http://www.w3.org/2006/vcard/ns#organization-name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + role: { + "@id": "http://www.w3.org/2006/vcard/ns#role", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + trustedApp: { + "@id": "http://www.w3.org/ns/auth/acl#trustedApp", + "@type": "@id", + "@isCollection": true, + }, + mode: { + "@id": "http://www.w3.org/ns/auth/acl#mode", + "@isCollection": true, + }, + Append: "http://www.w3.org/ns/auth/acl#Append", + Control: "http://www.w3.org/ns/auth/acl#Control", + Read: "http://www.w3.org/ns/auth/acl#Read", + Write: "http://www.w3.org/ns/auth/acl#Write", + origin: { + "@id": "http://www.w3.org/ns/auth/acl#origin", + "@type": "@id", + }, + key: { + "@id": "http://www.w3.org/ns/auth/cert#key", + "@type": "@id", + "@isCollection": true, + }, + modulus: { + "@id": "http://www.w3.org/ns/auth/cert#modulus", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + exponent: { + "@id": "http://www.w3.org/ns/auth/cert#exponent", + "@type": "http://www.w3.org/2001/XMLSchema#integer", + }, + inbox: { + "@id": "http://www.w3.org/ns/ldp#inbox", + "@type": "@id", + }, + preferencesFile: { + "@id": "http://www.w3.org/ns/pim/space#preferencesFile", + "@type": "@id", + }, + storage: { + "@id": "http://www.w3.org/ns/pim/space#storage", + "@type": "@id", + "@isCollection": true, + }, + account: { + "@id": "http://www.w3.org/ns/solid/terms#account", + "@type": "@id", + }, + privateTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#privateTypeIndex", + "@type": "@id", + "@isCollection": true, + }, + publicTypeIndex: { + "@id": "http://www.w3.org/ns/solid/terms#publicTypeIndex", + "@type": "@id", + "@isCollection": true, + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@isCollection": true, + }, +}; diff --git a/packages/connected/test/.ldo/solidProfile.schema.ts b/packages/connected/test/.ldo/solidProfile.schema.ts new file mode 100644 index 0000000..69466fc --- /dev/null +++ b/packages/connected/test/.ldo/solidProfile.schema.ts @@ -0,0 +1,749 @@ +import { Schema } from "shexj"; + +/** + * ============================================================================= + * solidProfileSchema: ShexJ Schema for solidProfile + * ============================================================================= + */ +export const solidProfileSchema: Schema = { + type: "Schema", + shapes: [ + { + id: "https://shaperepo.com/schemas/solidProfile#SolidProfileShape", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + valueExpr: { + type: "NodeConstraint", + values: ["http://schema.org/Person"], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Defines the node as a Person (from Schema.org)", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + valueExpr: { + type: "NodeConstraint", + values: ["http://xmlns.com/foaf/0.1/Person"], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Defines the node as a Person (from foaf)", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#fn", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The formatted name of a person. Example: John Smith", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/name", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "An alternate way to define a person's name.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#hasAddress", + valueExpr: + "https://shaperepo.com/schemas/solidProfile#AddressShape", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The person's street address.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#hasEmail", + valueExpr: + "https://shaperepo.com/schemas/solidProfile#EmailShape", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The person's email.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#hasPhoto", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "A link to the person's photo", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/img", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Photo link but in string form", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#hasTelephone", + valueExpr: + "https://shaperepo.com/schemas/solidProfile#PhoneNumberShape", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Person's telephone number", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#phone", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "An alternative way to define a person's telephone number using a string", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#organization-name", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The name of the organization with which the person is affiliated", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#role", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The name of the person's role in their organization", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/auth/acl#trustedApp", + valueExpr: + "https://shaperepo.com/schemas/solidProfile#TrustedAppShape", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A list of app origins that are trusted by this user", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/auth/cert#key", + valueExpr: + "https://shaperepo.com/schemas/solidProfile#RSAPublicKeyShape", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A list of RSA public keys that are associated with private keys the user holds.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/ldp#inbox", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The user's LDP inbox to which apps can post notifications", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/pim/space#preferencesFile", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The user's preferences", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/pim/space#storage", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The location of a Solid storage server related to this WebId", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/solid/terms#account", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The user's account", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/solid/terms#privateTypeIndex", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A registry of all types used on the user's Pod (for private access only)", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/solid/terms#publicTypeIndex", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A registry of all types used on the user's Pod (for public access)", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/knows", + valueExpr: + "https://shaperepo.com/schemas/solidProfile#SolidProfileShape", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A list of WebIds for all the people this user knows.", + }, + }, + ], + }, + ], + }, + extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], + }, + }, + { + id: "https://shaperepo.com/schemas/solidProfile#AddressShape", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#country-name", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The name of the user's country of residence", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#locality", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The name of the user's locality (City, Town etc.) of residence", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#postal-code", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The user's postal code", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#region", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The name of the user's region (State, Province etc.) of residence", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#street-address", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The user's street address", + }, + }, + ], + }, + ], + }, + }, + }, + { + id: "https://shaperepo.com/schemas/solidProfile#EmailShape", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + valueExpr: { + type: "NodeConstraint", + values: [ + "http://www.w3.org/2006/vcard/ns#Dom", + "http://www.w3.org/2006/vcard/ns#Home", + "http://www.w3.org/2006/vcard/ns#ISDN", + "http://www.w3.org/2006/vcard/ns#Internet", + "http://www.w3.org/2006/vcard/ns#Intl", + "http://www.w3.org/2006/vcard/ns#Label", + "http://www.w3.org/2006/vcard/ns#Parcel", + "http://www.w3.org/2006/vcard/ns#Postal", + "http://www.w3.org/2006/vcard/ns#Pref", + "http://www.w3.org/2006/vcard/ns#Work", + "http://www.w3.org/2006/vcard/ns#X400", + ], + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The type of email.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#value", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The value of an email as a mailto link (Example )", + }, + }, + ], + }, + ], + }, + extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], + }, + }, + { + id: "https://shaperepo.com/schemas/solidProfile#PhoneNumberShape", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + valueExpr: { + type: "NodeConstraint", + values: [ + "http://www.w3.org/2006/vcard/ns#Dom", + "http://www.w3.org/2006/vcard/ns#Home", + "http://www.w3.org/2006/vcard/ns#ISDN", + "http://www.w3.org/2006/vcard/ns#Internet", + "http://www.w3.org/2006/vcard/ns#Intl", + "http://www.w3.org/2006/vcard/ns#Label", + "http://www.w3.org/2006/vcard/ns#Parcel", + "http://www.w3.org/2006/vcard/ns#Postal", + "http://www.w3.org/2006/vcard/ns#Pref", + "http://www.w3.org/2006/vcard/ns#Work", + "http://www.w3.org/2006/vcard/ns#X400", + ], + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "They type of Phone Number", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/2006/vcard/ns#value", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "The value of a phone number as a tel link (Example )", + }, + }, + ], + }, + ], + }, + extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], + }, + }, + { + id: "https://shaperepo.com/schemas/solidProfile#TrustedAppShape", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/auth/acl#mode", + valueExpr: { + type: "NodeConstraint", + values: [ + "http://www.w3.org/ns/auth/acl#Append", + "http://www.w3.org/ns/auth/acl#Control", + "http://www.w3.org/ns/auth/acl#Read", + "http://www.w3.org/ns/auth/acl#Write", + ], + }, + min: 1, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The level of access provided to this origin", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/auth/acl#origin", + valueExpr: { + type: "NodeConstraint", + nodeKind: "iri", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "The app origin the user trusts", + }, + }, + ], + }, + ], + }, + }, + }, + { + id: "https://shaperepo.com/schemas/solidProfile#RSAPublicKeyShape", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/auth/cert#modulus", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "RSA Modulus", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://www.w3.org/ns/auth/cert#exponent", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#integer", + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "RSA Exponent", + }, + }, + ], + }, + ], + }, + }, + }, + ], +}; diff --git a/packages/connected/test/.ldo/solidProfile.shapeTypes.ts b/packages/connected/test/.ldo/solidProfile.shapeTypes.ts new file mode 100644 index 0000000..71426e4 --- /dev/null +++ b/packages/connected/test/.ldo/solidProfile.shapeTypes.ts @@ -0,0 +1,71 @@ +import { ShapeType } from "@ldo/ldo"; +import { solidProfileSchema } from "./solidProfile.schema"; +import { solidProfileContext } from "./solidProfile.context"; +import { + SolidProfileShape, + AddressShape, + EmailShape, + PhoneNumberShape, + TrustedAppShape, + RSAPublicKeyShape, +} from "./solidProfile.typings"; + +/** + * ============================================================================= + * LDO ShapeTypes solidProfile + * ============================================================================= + */ + +/** + * SolidProfileShape ShapeType + */ +export const SolidProfileShapeShapeType: ShapeType = { + schema: solidProfileSchema, + shape: "https://shaperepo.com/schemas/solidProfile#SolidProfileShape", + context: solidProfileContext, +}; + +/** + * AddressShape ShapeType + */ +export const AddressShapeShapeType: ShapeType = { + schema: solidProfileSchema, + shape: "https://shaperepo.com/schemas/solidProfile#AddressShape", + context: solidProfileContext, +}; + +/** + * EmailShape ShapeType + */ +export const EmailShapeShapeType: ShapeType = { + schema: solidProfileSchema, + shape: "https://shaperepo.com/schemas/solidProfile#EmailShape", + context: solidProfileContext, +}; + +/** + * PhoneNumberShape ShapeType + */ +export const PhoneNumberShapeShapeType: ShapeType = { + schema: solidProfileSchema, + shape: "https://shaperepo.com/schemas/solidProfile#PhoneNumberShape", + context: solidProfileContext, +}; + +/** + * TrustedAppShape ShapeType + */ +export const TrustedAppShapeShapeType: ShapeType = { + schema: solidProfileSchema, + shape: "https://shaperepo.com/schemas/solidProfile#TrustedAppShape", + context: solidProfileContext, +}; + +/** + * RSAPublicKeyShape ShapeType + */ +export const RSAPublicKeyShapeShapeType: ShapeType = { + schema: solidProfileSchema, + shape: "https://shaperepo.com/schemas/solidProfile#RSAPublicKeyShape", + context: solidProfileContext, +}; diff --git a/packages/connected/test/.ldo/solidProfile.typings.ts b/packages/connected/test/.ldo/solidProfile.typings.ts new file mode 100644 index 0000000..95dbaef --- /dev/null +++ b/packages/connected/test/.ldo/solidProfile.typings.ts @@ -0,0 +1,293 @@ +import { LdoJsonldContext, LdSet } from "@ldo/ldo"; + +/** + * ============================================================================= + * Typescript Typings for solidProfile + * ============================================================================= + */ + +/** + * SolidProfileShape Type + */ +export interface SolidProfileShape { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * Defines the node as a Person (from Schema.org) | Defines the node as a Person (from foaf) + */ + type: LdSet< + | { + "@id": "Person"; + } + | { + "@id": "Person2"; + } + >; + /** + * The formatted name of a person. Example: John Smith + */ + fn?: string; + /** + * An alternate way to define a person's name. + */ + name?: string; + /** + * The person's street address. + */ + hasAddress?: LdSet; + /** + * The person's email. + */ + hasEmail?: LdSet; + /** + * A link to the person's photo + */ + hasPhoto?: { + "@id": string; + }; + /** + * Photo link but in string form + */ + img?: string; + /** + * Person's telephone number + */ + hasTelephone?: LdSet; + /** + * An alternative way to define a person's telephone number using a string + */ + phone?: string; + /** + * The name of the organization with which the person is affiliated + */ + organizationName?: string; + /** + * The name of the person's role in their organization + */ + role?: string; + /** + * A list of app origins that are trusted by this user + */ + trustedApp?: LdSet; + /** + * A list of RSA public keys that are associated with private keys the user holds. + */ + key?: LdSet; + /** + * The user's LDP inbox to which apps can post notifications + */ + inbox: { + "@id": string; + }; + /** + * The user's preferences + */ + preferencesFile?: { + "@id": string; + }; + /** + * The location of a Solid storage server related to this WebId + */ + storage?: LdSet<{ + "@id": string; + }>; + /** + * The user's account + */ + account?: { + "@id": string; + }; + /** + * A registry of all types used on the user's Pod (for private access only) + */ + privateTypeIndex?: LdSet<{ + "@id": string; + }>; + /** + * A registry of all types used on the user's Pod (for public access) + */ + publicTypeIndex?: LdSet<{ + "@id": string; + }>; + /** + * A list of WebIds for all the people this user knows. + */ + knows?: LdSet; +} + +/** + * AddressShape Type + */ +export interface AddressShape { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * The name of the user's country of residence + */ + countryName?: string; + /** + * The name of the user's locality (City, Town etc.) of residence + */ + locality?: string; + /** + * The user's postal code + */ + postalCode?: string; + /** + * The name of the user's region (State, Province etc.) of residence + */ + region?: string; + /** + * The user's street address + */ + streetAddress?: string; +} + +/** + * EmailShape Type + */ +export interface EmailShape { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * The type of email. + */ + type?: + | { + "@id": "Dom"; + } + | { + "@id": "Home"; + } + | { + "@id": "ISDN"; + } + | { + "@id": "Internet"; + } + | { + "@id": "Intl"; + } + | { + "@id": "Label"; + } + | { + "@id": "Parcel"; + } + | { + "@id": "Postal"; + } + | { + "@id": "Pref"; + } + | { + "@id": "Work"; + } + | { + "@id": "X400"; + }; + /** + * The value of an email as a mailto link (Example ) + */ + value: { + "@id": string; + }; +} + +/** + * PhoneNumberShape Type + */ +export interface PhoneNumberShape { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * They type of Phone Number + */ + type?: + | { + "@id": "Dom"; + } + | { + "@id": "Home"; + } + | { + "@id": "ISDN"; + } + | { + "@id": "Internet"; + } + | { + "@id": "Intl"; + } + | { + "@id": "Label"; + } + | { + "@id": "Parcel"; + } + | { + "@id": "Postal"; + } + | { + "@id": "Pref"; + } + | { + "@id": "Work"; + } + | { + "@id": "X400"; + }; + /** + * The value of a phone number as a tel link (Example ) + */ + value: { + "@id": string; + }; +} + +/** + * TrustedAppShape Type + */ +export interface TrustedAppShape { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * The level of access provided to this origin + */ + mode: LdSet< + | { + "@id": "Append"; + } + | { + "@id": "Control"; + } + | { + "@id": "Read"; + } + | { + "@id": "Write"; + } + >; + /** + * The app origin the user trusts + */ + origin: { + "@id": string; + }; +} + +/** + * RSAPublicKeyShape Type + */ +export interface RSAPublicKeyShape { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * RSA Modulus + */ + modulus: string; + /** + * RSA Exponent + */ + exponent: number; +} diff --git a/packages/connected/test/.shapes/solidProfile.shex b/packages/connected/test/.shapes/solidProfile.shex new file mode 100644 index 0000000..cbc068c --- /dev/null +++ b/packages/connected/test/.shapes/solidProfile.shex @@ -0,0 +1,121 @@ +PREFIX srs: +PREFIX foaf: +PREFIX rdfs: +PREFIX schem: +PREFIX vcard: +PREFIX xsd: +PREFIX acl: +PREFIX cert: +PREFIX ldp: +PREFIX sp: +PREFIX solid: + +srs:SolidProfileShape EXTRA a { + a [ schem:Person ] + // rdfs:comment "Defines the node as a Person (from Schema.org)" ; + a [ foaf:Person ] + // rdfs:comment "Defines the node as a Person (from foaf)" ; + vcard:fn xsd:string ? + // rdfs:comment "The formatted name of a person. Example: John Smith" ; + foaf:name xsd:string ? + // rdfs:comment "An alternate way to define a person's name." ; + vcard:hasAddress @srs:AddressShape * + // rdfs:comment "The person's street address." ; + vcard:hasEmail @srs:EmailShape * + // rdfs:comment "The person's email." ; + vcard:hasPhoto IRI ? + // rdfs:comment "A link to the person's photo" ; + foaf:img xsd:string ? + // rdfs:comment "Photo link but in string form" ; + vcard:hasTelephone @srs:PhoneNumberShape * + // rdfs:comment "Person's telephone number" ; + vcard:phone xsd:string ? + // rdfs:comment "An alternative way to define a person's telephone number using a string" ; + vcard:organization-name xsd:string ? + // rdfs:comment "The name of the organization with which the person is affiliated" ; + vcard:role xsd:string ? + // rdfs:comment "The name of the person's role in their organization" ; + acl:trustedApp @srs:TrustedAppShape * + // rdfs:comment "A list of app origins that are trusted by this user" ; + cert:key @srs:RSAPublicKeyShape * + // rdfs:comment "A list of RSA public keys that are associated with private keys the user holds." ; + ldp:inbox IRI + // rdfs:comment "The user's LDP inbox to which apps can post notifications" ; + sp:preferencesFile IRI ? + // rdfs:comment "The user's preferences" ; + sp:storage IRI * + // rdfs:comment "The location of a Solid storage server related to this WebId" ; + solid:account IRI ? + // rdfs:comment "The user's account" ; + solid:privateTypeIndex IRI * + // rdfs:comment "A registry of all types used on the user's Pod (for private access only)" ; + solid:publicTypeIndex IRI * + // rdfs:comment "A registry of all types used on the user's Pod (for public access)" ; + foaf:knows @srs:SolidProfileShape * + // rdfs:comment "A list of WebIds for all the people this user knows." ; +} + +srs:AddressShape { + vcard:country-name xsd:string ? + // rdfs:comment "The name of the user's country of residence" ; + vcard:locality xsd:string ? + // rdfs:comment "The name of the user's locality (City, Town etc.) of residence" ; + vcard:postal-code xsd:string ? + // rdfs:comment "The user's postal code" ; + vcard:region xsd:string ? + // rdfs:comment "The name of the user's region (State, Province etc.) of residence" ; + vcard:street-address xsd:string ? + // rdfs:comment "The user's street address" ; +} + +srs:EmailShape EXTRA a { + a [ + vcard:Dom + vcard:Home + vcard:ISDN + vcard:Internet + vcard:Intl + vcard:Label + vcard:Parcel + vcard:Postal + vcard:Pref + vcard:Work + vcard:X400 + ] ? + // rdfs:comment "The type of email." ; + vcard:value IRI + // rdfs:comment "The value of an email as a mailto link (Example )" ; +} + +srs:PhoneNumberShape EXTRA a { + a [ + vcard:Dom + vcard:Home + vcard:ISDN + vcard:Internet + vcard:Intl + vcard:Label + vcard:Parcel + vcard:Postal + vcard:Pref + vcard:Work + vcard:X400 + ] ? + // rdfs:comment "They type of Phone Number" ; + vcard:value IRI + // rdfs:comment "The value of a phone number as a tel link (Example )" ; +} + +srs:TrustedAppShape { + acl:mode [acl:Append acl:Control acl:Read acl:Write] + + // rdfs:comment "The level of access provided to this origin" ; + acl:origin IRI + // rdfs:comment "The app origin the user trusts" +} + +srs:RSAPublicKeyShape { + cert:modulus xsd:string + // rdfs:comment "RSA Modulus" ; + cert:exponent xsd:integer + // rdfs:comment "RSA Exponent" ; +} \ No newline at end of file diff --git a/packages/connected/test/LinkTraversalData.ts b/packages/connected/test/LinkTraversalData.ts new file mode 100644 index 0000000..0377b2a --- /dev/null +++ b/packages/connected/test/LinkTraversalData.ts @@ -0,0 +1,42 @@ +import type { ResourceInfo } from "@ldo/test-solid-server"; + +export const BASE_CONTAINER = "http://localhost:3005/test-container/"; +export const MAIN_PROFILE_URI = `${BASE_CONTAINER}mainProfile.ttl`; +export const MAIN_PROFILE_SUBJECT = `${MAIN_PROFILE_URI}#me`; +export const OTHER_PROFILE_URI = `${BASE_CONTAINER}otherProfile.ttl`; +export const OTHER_PROFILE_SUBJECT = `${OTHER_PROFILE_URI}#me`; + +export const linkTraversalData: ResourceInfo = { + slug: "test-container/", + isContainer: true, + contains: [ + { + slug: "mainProfile.ttl", + isContainer: false, + mimeType: "text/ttl", + data: ` + @prefix foaf: . + @prefix : <#> . + + :me a foaf:Person ; + foaf:name "Main User" ; + foaf:mbox ; + foaf:knows . + `, + }, + { + slug: "otherProfile.ttl", + isContainer: false, + mimeType: "text/ttl", + data: ` + @prefix foaf: . + @prefix : <#> . + + :me a foaf:Person ; + foaf:name "Other User" ; + foaf:mbox ; + foaf:knows . + `, + }, + ], +}; diff --git a/packages/connected/test/LinkTraversalIntegration.test.ts b/packages/connected/test/LinkTraversalIntegration.test.ts index d6d199b..1669b13 100644 --- a/packages/connected/test/LinkTraversalIntegration.test.ts +++ b/packages/connected/test/LinkTraversalIntegration.test.ts @@ -1,101 +1,51 @@ -import type { App } from "@solid/community-server"; -import type { ConnectedLdoDataset } from "../src"; -import { ROOT_CONTAINER, WEB_ID, createApp } from "./solidServer.helper"; -import { generateAuthFetch } from "./authFetch.helper"; -import type { SolidConnectedPlugin } from "@ldo/connected-solid"; +import type { ConnectedLdoDataset } from "../src/ConnectedLdoDataset"; +import { createConnectedLdoDataset } from "../src"; +import { + solidConnectedPlugin, + type SolidConnectedPlugin, +} from "@ldo/connected-solid"; +import { setupServer } from "@ldo/test-solid-server"; +import { + linkTraversalData, + MAIN_PROFILE_SUBJECT, + MAIN_PROFILE_URI, + OTHER_PROFILE_URI, +} from "./LinkTraversalData"; +import { SolidProfileShapeShapeType } from "./.ldo/solidProfile.shapeTypes"; describe("Link Traversal", () => { - let app: App; - let authFetch: typeof fetch; - let fetchMock: jest.Mock< - Promise, - [input: RequestInfo | URL, init?: RequestInit | undefined] - >; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore let solidLdoDataset: ConnectedLdoDataset; - let previousJestId: string | undefined; - let previousNodeEnv: string | undefined; - beforeAll(async () => { - // Remove Jest ID so that community solid server doesn't use the Jest Import - previousJestId = process.env.JEST_WORKER_ID; - previousNodeEnv = process.env.NODE_ENV; - delete process.env.JEST_WORKER_ID; - process.env.NODE_ENV = "other_test"; - // Start up the server - app = await createApp(); - await app.start(); - authFetch = await generateAuthFetch(); - }); - - afterAll(async () => { - app.stop(); - process.env.JEST_WORKER_ID = previousJestId; - process.env.NODE_ENV = previousNodeEnv; - const testDataPath = path.join(__dirname, "./data"); - await fs.rm(testDataPath, { recursive: true, force: true }); - }); + const s = setupServer(3005, linkTraversalData); beforeEach(async () => { - fetchMock = jest.fn(authFetch); - solidLdoDataset = createSolidLdoDataset(); - solidLdoDataset.setContext("solid", { fetch: fetchMock }); - // Create a new document called sample.ttl - await authFetch(ROOT_CONTAINER, { - method: "POST", - headers: { - link: '; rel="type"', - 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", - headers: { "content-type": "text/turtle", slug: "sample.ttl" }, - body: SPIDER_MAN_TTL, - }), - authFetch(TEST_CONTAINER_URI, { - method: "POST", - 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, - }), - ]); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + solidLdoDataset = createConnectedLdoDataset([solidConnectedPlugin]); + solidLdoDataset.setContext("solid", { fetch: s.fetchMock }); }); - afterEach(async () => { - await Promise.all([ - authFetch(SAMPLE_DATA_URI, { - method: "DELETE", - }), - authFetch(SAMPLE2_DATA_URI, { - method: "DELETE", - }), - authFetch(SAMPLE_BINARY_URI, { - method: "DELETE", - }), - authFetch(SAMPLE2_BINARY_URI, { - method: "DELETE", - }), - authFetch(SAMPLE_PROFILE_URI, { - method: "DELETE", - }), - authFetch(SAMPLE_CONTAINER_URI, { - method: "DELETE", - }), - ]); - await authFetch(TEST_CONTAINER_URI, { - method: "DELETE", - }); + it("does a simple run to traverse data", async () => { + const mainProfileResource = solidLdoDataset.getResource(MAIN_PROFILE_URI); + const data = await solidLdoDataset + .usingType(SolidProfileShapeShapeType) + .startLinkQuery(mainProfileResource, MAIN_PROFILE_SUBJECT, { + name: true, + knows: { + name: true, + }, + }) + .run(); + const resourceUris = solidLdoDataset + .getResources() + .map((resource) => resource.uri); + console.log(resourceUris); + expect(resourceUris.length).toBe(2); + expect(resourceUris).toContain(MAIN_PROFILE_URI); + expect(resourceUris).toContain(OTHER_PROFILE_URI); + expect(data.name).toBe("Main User"); + expect(data.knows?.toArray()[0].name).toBe("Other User"); }); }); diff --git a/packages/connected/test/authFetch.helper.ts b/packages/connected/test/authFetch.helper.ts deleted file mode 100644 index fffee6a..0000000 --- a/packages/connected/test/authFetch.helper.ts +++ /dev/null @@ -1,112 +0,0 @@ -import type { KeyPair } from "@inrupt/solid-client-authn-core"; -import { - buildAuthenticatedFetch, - createDpopHeader, - generateDpopKeyPair, -} from "@inrupt/solid-client-authn-core"; -import fetch from "cross-fetch"; - -const config = { - podName: process.env.USER_NAME || "example", - email: process.env.EMAIL || "hello@example.com", - password: process.env.PASSWORD || "abc123", -}; - -async function getAuthorization(): Promise { - // First we request the account API controls to find out where we can log in - const indexResponse = await fetch("http://localhost:3001/.account/"); - const { controls } = await indexResponse.json(); - - // And then we log in to the account API - const response = await fetch(controls.password.login, { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ - email: config.email, - password: config.password, - }), - }); - // This authorization value will be used to authenticate in the next step - const result = await response.json(); - return result.authorization; -} - -async function getSecret( - authorization: string, -): Promise<{ id: string; secret: string; resource: string }> { - // Now that we are logged in, we need to request the updated controls from the server. - // These will now have more values than in the previous example. - const indexResponse = await fetch("http://localhost:3001/.account/", { - headers: { authorization: `CSS-Account-Token ${authorization}` }, - }); - const { controls } = await indexResponse.json(); - - // Here we request the server to generate a token on our account - const response = await fetch(controls.account.clientCredentials, { - method: "POST", - headers: { - authorization: `CSS-Account-Token ${authorization}`, - "content-type": "application/json", - }, - // The name field will be used when generating the ID of your token. - // The WebID field determines which WebID you will identify as when using the token. - // Only WebIDs linked to your account can be used. - body: JSON.stringify({ - name: "my-token", - webId: `http://localhost:3001/${config.podName}/profile/card#me`, - }), - }); - - // These are the identifier and secret of your token. - // Store the secret somewhere safe as there is no way to request it again from the server! - // The `resource` value can be used to delete the token at a later point in time. - const response2 = await response.json(); - return response2; -} - -async function getAccessToken( - id: string, - secret: string, -): Promise<{ accessToken: string; dpopKey: KeyPair }> { - try { - // A key pair is needed for encryption. - // This function from `solid-client-authn` generates such a pair for you. - const dpopKey = await generateDpopKeyPair(); - - // These are the ID and secret generated in the previous step. - // Both the ID and the secret need to be form-encoded. - const authString = `${encodeURIComponent(id)}:${encodeURIComponent( - secret, - )}`; - // This URL can be found by looking at the "token_endpoint" field at - // http://localhost:3001/.well-known/openid-configuration - // if your server is hosted at http://localhost:3000/. - const tokenUrl = "http://localhost:3001/.oidc/token"; - const response = await fetch(tokenUrl, { - method: "POST", - headers: { - // The header needs to be in base64 encoding. - authorization: `Basic ${Buffer.from(authString).toString("base64")}`, - "content-type": "application/x-www-form-urlencoded", - dpop: await createDpopHeader(tokenUrl, "POST", dpopKey), - }, - body: "grant_type=client_credentials&scope=webid", - }); - - // This is the Access token that will be used to do an authenticated request to the server. - // The JSON also contains an "expires_in" field in seconds, - // which you can use to know when you need request a new Access token. - const response2 = await response.json(); - return { accessToken: response2.access_token, dpopKey }; - } catch (err) { - console.error(err); - throw err; - } -} - -export async function generateAuthFetch() { - const authorization = await getAuthorization(); - const { id, secret } = await getSecret(authorization); - const { accessToken, dpopKey } = await getAccessToken(id, secret); - return await buildAuthenticatedFetch(accessToken, { dpopKey }); -} diff --git a/packages/connected/test/configs/server-config-without-websocket.json b/packages/connected/test/configs/server-config-without-websocket.json deleted file mode 100644 index 626d082..0000000 --- a/packages/connected/test/configs/server-config-without-websocket.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "import": [ - "css:config/app/init/initialize-root.json", - "css:config/app/main/default.json", - "css:config/app/variables/default.json", - "css:config/http/handler/default.json", - "css:config/http/middleware/default.json", - "css:config/http/notifications/webhooks.json", - "css:config/http/server-factory/http.json", - "css:config/http/static/default.json", - "css:config/identity/access/public.json", - "css:config/identity/email/default.json", - "css:config/identity/handler/no-accounts.json", - "css:config/identity/oidc/default.json", - "css:config/identity/ownership/token.json", - "css:config/identity/pod/static.json", - "css:config/ldp/authentication/dpop-bearer.json", - "css:config/ldp/authorization/webacl.json", - "css:config/ldp/handler/default.json", - "css:config/ldp/metadata-parser/default.json", - "css:config/ldp/metadata-writer/default.json", - "css:config/ldp/modes/default.json", - "css:config/storage/backend/file.json", - "css:config/storage/key-value/resource-store.json", - "css:config/storage/location/root.json", - "css:config/storage/middleware/default.json", - "css:config/util/auxiliary/acl.json", - "css:config/util/identifiers/suffix.json", - "css:config/util/index/default.json", - "css:config/util/logging/winston.json", - "css:config/util/representation-conversion/default.json", - "css:config/util/resource-locker/file.json", - "css:config/util/variables/default.json" - ], - "@graph": [ - { - "comment": [ - "A Solid server that stores its resources on disk and uses WAC for authorization.", - "No registration and the root container is initialized to allow full access for everyone so make sure to change this." - ] - } - ] -} \ No newline at end of file diff --git a/packages/connected/test/configs/server-config.json b/packages/connected/test/configs/server-config.json deleted file mode 100644 index 5e96784..0000000 --- a/packages/connected/test/configs/server-config.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld" - ], - "import": [ - "css:config/app/init/static-root.json", - "css:config/app/main/default.json", - "css:config/app/variables/default.json", - "css:config/http/handler/default.json", - "css:config/http/middleware/default.json", - "css:config/http/notifications/all.json", - "css:config/http/server-factory/http.json", - "css:config/http/static/default.json", - "css:config/identity/access/public.json", - "css:config/identity/email/default.json", - "css:config/identity/handler/default.json", - "css:config/identity/oidc/default.json", - "css:config/identity/ownership/token.json", - "css:config/identity/pod/static.json", - "css:config/ldp/authentication/dpop-bearer.json", - "css:config/ldp/authorization/webacl.json", - "css:config/ldp/handler/default.json", - "css:config/ldp/metadata-parser/default.json", - "css:config/ldp/metadata-writer/default.json", - "css:config/ldp/modes/default.json", - "css:config/storage/backend/memory.json", - "css:config/storage/key-value/resource-store.json", - "css:config/storage/location/pod.json", - "css:config/storage/middleware/default.json", - "css:config/util/auxiliary/acl.json", - "css:config/util/identifiers/suffix.json", - "css:config/util/index/default.json", - "css:config/util/logging/winston.json", - "css:config/util/representation-conversion/default.json", - "css:config/util/resource-locker/memory.json", - "css:config/util/variables/default.json" - ], - "@graph": [ - { - "comment": "A Solid server that stores its resources on disk and uses WAC for authorization." - } - ] -} \ No newline at end of file diff --git a/packages/connected/test/configs/solid-css-seed.json b/packages/connected/test/configs/solid-css-seed.json deleted file mode 100644 index 5894d0d..0000000 --- a/packages/connected/test/configs/solid-css-seed.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "email": "hello@example.com", - "password": "abc123", - "pods": [ - { "name": "example" } - ] - } -] \ No newline at end of file diff --git a/packages/connected/test/setup-tests.ts b/packages/connected/test/setup-tests.ts deleted file mode 100644 index f4378ae..0000000 --- a/packages/connected/test/setup-tests.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { config } from "dotenv"; - -config(); diff --git a/packages/connected/test/solidServer.helper.ts b/packages/connected/test/solidServer.helper.ts deleted file mode 100644 index 848abc1..0000000 --- a/packages/connected/test/solidServer.helper.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Taken from https://github.com/comunica/comunica/blob/b237be4265c353a62a876187d9e21e3bc05123a3/engines/query-sparql/test/QuerySparql-solid-test.ts#L9 - -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}` as SolidContainerUri; -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); - -export async function createApp(customConfigPath?: string): Promise { - if (process.env.SERVER) { - return { - start: () => {}, - stop: () => {}, - } as App; - } - const appRunner = new AppRunner(); - - return appRunner.create({ - loaderProperties: { - mainModulePath: resolveModulePath(""), - typeChecking: false, - }, - config: customConfigPath ?? resolveModulePath("config/file-root.json"), - variableBindings: {}, - shorthand: { - port: 3_001, - loggingLevel: "off", - seedConfig: path.join(__dirname, "configs", "solid-css-seed.json"), - rootFilePath: path.join(__dirname, "./data"), - }, - }); -} diff --git a/packages/test-solid-server/Readme.md b/packages/test-solid-server/Readme.md index 996e0ec..01ef5e9 100644 --- a/packages/test-solid-server/Readme.md +++ b/packages/test-solid-server/Readme.md @@ -1,3 +1,17 @@ # @ldo/test-solid-server -This is a reusable Solid Server to be used in Jest integration tests. \ No newline at end of file +This is a reusable Solid Server to be used in Jest integration tests. + +## Setup + +Install cross-env + +``` +npm i --save-dev cross-env +``` + +Use the following to run your tests + +``` +"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage", +``` \ No newline at end of file diff --git a/packages/test-solid-server/src/authFetch.ts b/packages/test-solid-server/src/authFetch.ts index d86a0ab..7a2838e 100644 --- a/packages/test-solid-server/src/authFetch.ts +++ b/packages/test-solid-server/src/authFetch.ts @@ -81,7 +81,7 @@ async function getAccessToken( secret, )}`; // This URL can be found by looking at the "token_endpoint" field at - // http://localhost:3001/.well-known/openid-configuration + // http://localhost:PORT/.well-known/openid-configuration // if your server is hosted at http://localhost:3000/. const tokenUrl = `http://localhost:${port}/.oidc/token`; const response = await fetch(tokenUrl, {