parent
1f9388871d
commit
6915dcadce
@ -0,0 +1,3 @@ |
||||
{ |
||||
"extends": ["../../.eslintrc"] |
||||
} |
@ -0,0 +1 @@ |
||||
test/test-server/data |
@ -0,0 +1,21 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2023 Jackson Morgan |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,14 @@ |
||||
# @ldo/solid-type-index |
||||
|
||||
Alpha |
||||
|
||||
// TODO: Write readme |
||||
|
||||
## Sponsorship |
||||
This project was made possible by a grant from NGI Zero Entrust via nlnet. Learn more on the [NLnet project page](https://nlnet.nl/project/SolidUsableApps/). |
||||
|
||||
[<img src="https://nlnet.nl/logo/banner.png" alt="nlnet foundation logo" width="300" />](https://nlnet.nl/) |
||||
[<img src="https://nlnet.nl/image/logos/NGI0Entrust_tag.svg" alt="NGI Zero Entrust Logo" width="300" />](https://nlnet.nl/) |
||||
|
||||
## Liscense |
||||
MIT |
@ -0,0 +1,6 @@ |
||||
const sharedConfig = require("../../jest.config.js"); |
||||
module.exports = { |
||||
...sharedConfig, |
||||
rootDir: "./", |
||||
testEnvironment: "jsdom", |
||||
}; |
@ -0,0 +1,2 @@ |
||||
import "@inrupt/jest-jsdom-polyfills"; |
||||
globalThis.fetch = async () => new Response(); |
@ -0,0 +1,50 @@ |
||||
{ |
||||
"name": "@ldo/solid-type-index", |
||||
"version": "0.0.1-alpha.28", |
||||
"description": "Solid Type Index support for LDO", |
||||
"main": "dist/index.js", |
||||
"scripts": { |
||||
"build": "tsc --project tsconfig.build.json", |
||||
"watch": "tsc --watch", |
||||
"test": "npm run test:integration", |
||||
"test:watch": "jest --watch", |
||||
"prepublishOnly": "npm run test && npm run build", |
||||
"build:ldo": "ldo build --input src/.shapes --output src/.ldo", |
||||
"lint": "eslint src/** --fix --no-error-on-unmatched-pattern", |
||||
"test:integration": "start-server-and-test start-test-server http://localhost:3003 start-integration-test", |
||||
"start-test-server": "ts-node ./test/test-server/runServer.ts", |
||||
"start-integration-test": "jest --coverage" |
||||
}, |
||||
"repository": { |
||||
"type": "git", |
||||
"url": "git+https://github.com/o-development/ldobjects.git" |
||||
}, |
||||
"author": "Jackson Morgan", |
||||
"license": "MIT", |
||||
"bugs": { |
||||
"url": "https://github.com/o-development/ldobjects/issues" |
||||
}, |
||||
"homepage": "https://github.com/o-development/ldobjects/tree/main/packages/solid-react#readme", |
||||
"devDependencies": { |
||||
"@ldo/rdf-utils": "^0.0.1-alpha.24", |
||||
"@rdfjs/types": "^1.0.1", |
||||
"@testing-library/react": "^14.1.2", |
||||
"@types/jest": "^27.0.3", |
||||
"jest-environment-jsdom": "^27.0.0", |
||||
"start-server-and-test": "^2.0.3", |
||||
"ts-jest": "^27.1.2", |
||||
"ts-node": "^10.9.2" |
||||
}, |
||||
"dependencies": { |
||||
"@ldo/solid": "^0.0.1-alpha.28", |
||||
"@ldo/solid-react": "^0.0.1-alpha.28" |
||||
}, |
||||
"files": [ |
||||
"dist", |
||||
"src" |
||||
], |
||||
"publishConfig": { |
||||
"access": "public" |
||||
}, |
||||
"gitHead": "c63f055aab22155b60a5fdee4172979b9c287dfa" |
||||
} |
@ -0,0 +1,19 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* profileContext: JSONLD Context for profile |
||||
* ============================================================================= |
||||
*/ |
||||
export const profileContext: ContextDefinition = { |
||||
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, |
||||
}, |
||||
}; |
@ -0,0 +1,64 @@ |
||||
import { Schema } from "shexj"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* profileSchema: ShexJ Schema for profile |
||||
* ============================================================================= |
||||
*/ |
||||
export const profileSchema: Schema = { |
||||
type: "Schema", |
||||
shapes: [ |
||||
{ |
||||
id: "https://shaperepo.com/schemas/solidProfile#TypeIndexProfile", |
||||
type: "ShapeDecl", |
||||
shapeExpr: { |
||||
type: "Shape", |
||||
expression: { |
||||
type: "EachOf", |
||||
expressions: [ |
||||
{ |
||||
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)", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
}, |
||||
}, |
||||
], |
||||
}; |
@ -0,0 +1,19 @@ |
||||
import { ShapeType } from "@ldo/ldo"; |
||||
import { profileSchema } from "./profile.schema"; |
||||
import { profileContext } from "./profile.context"; |
||||
import { TypeIndexProfile } from "./profile.typings"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* LDO ShapeTypes profile |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* TypeIndexProfile ShapeType |
||||
*/ |
||||
export const TypeIndexProfileShapeType: ShapeType<TypeIndexProfile> = { |
||||
schema: profileSchema, |
||||
shape: "https://shaperepo.com/schemas/solidProfile#TypeIndexProfile", |
||||
context: profileContext, |
||||
}; |
@ -0,0 +1,27 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* Typescript Typings for profile |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* TypeIndexProfile Type |
||||
*/ |
||||
export interface TypeIndexProfile { |
||||
"@id"?: string; |
||||
"@context"?: ContextDefinition; |
||||
/** |
||||
* A registry of all types used on the user's Pod (for private access only) |
||||
*/ |
||||
privateTypeIndex?: { |
||||
"@id": string; |
||||
}[]; |
||||
/** |
||||
* A registry of all types used on the user's Pod (for public access) |
||||
*/ |
||||
publicTypeIndex?: { |
||||
"@id": string; |
||||
}[]; |
||||
} |
@ -0,0 +1,47 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* typeIndexContext: JSONLD Context for typeIndex |
||||
* ============================================================================= |
||||
*/ |
||||
export const typeIndexContext: ContextDefinition = { |
||||
TypeIndex: { |
||||
"@id": "http://www.w3.org/ns/solid/terms#TypeIndex", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
}, |
||||
}, |
||||
ListedDocument: { |
||||
"@id": "http://www.w3.org/ns/solid/terms#ListedDocument", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
}, |
||||
}, |
||||
TypeRegistration: { |
||||
"@id": "http://www.w3.org/ns/solid/terms#TypeRegistration", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
forClass: { |
||||
"@id": "http://www.w3.org/ns/solid/terms#forClass", |
||||
"@type": "@id", |
||||
}, |
||||
instance: { |
||||
"@id": "http://www.w3.org/ns/solid/terms#instance", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
instanceContainer: { |
||||
"@id": "http://www.w3.org/ns/solid/terms#instanceContainer", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
}, |
||||
}, |
||||
}; |
@ -0,0 +1,144 @@ |
||||
import { Schema } from "shexj"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* typeIndexSchema: ShexJ Schema for typeIndex |
||||
* ============================================================================= |
||||
*/ |
||||
export const typeIndexSchema: Schema = { |
||||
type: "Schema", |
||||
shapes: [ |
||||
{ |
||||
id: "https://shaperepo.com/schemas/solidProfile#TypeIndexDocument", |
||||
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/ns/solid/terms#TypeIndex"], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Defines the node as a TypeIndex", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: ["http://www.w3.org/ns/solid/terms#ListedDocument"], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Defines the node as a Listed Document", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||
}, |
||||
}, |
||||
{ |
||||
id: "https://shaperepo.com/schemas/solidProfile#TypeRegistration", |
||||
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/ns/solid/terms#TypeRegistration"], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Defines this node as a Type Registration", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/ns/solid/terms#forClass", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
nodeKind: "iri", |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "The class of object at this type.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/ns/solid/terms#instance", |
||||
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 specific resource that contains the class.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/ns/solid/terms#instanceContainer", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
nodeKind: "iri", |
||||
}, |
||||
min: 0, |
||||
max: -1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Containers that contain resources with the class.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||
}, |
||||
}, |
||||
], |
||||
}; |
@ -0,0 +1,28 @@ |
||||
import { ShapeType } from "@ldo/ldo"; |
||||
import { typeIndexSchema } from "./typeIndex.schema"; |
||||
import { typeIndexContext } from "./typeIndex.context"; |
||||
import { TypeIndexDocument, TypeRegistration } from "./typeIndex.typings"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* LDO ShapeTypes typeIndex |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* TypeIndexDocument ShapeType |
||||
*/ |
||||
export const TypeIndexDocumentShapeType: ShapeType<TypeIndexDocument> = { |
||||
schema: typeIndexSchema, |
||||
shape: "https://shaperepo.com/schemas/solidProfile#TypeIndexDocument", |
||||
context: typeIndexContext, |
||||
}; |
||||
|
||||
/** |
||||
* TypeRegistration ShapeType |
||||
*/ |
||||
export const TypeRegistrationShapeType: ShapeType<TypeRegistration> = { |
||||
schema: typeIndexSchema, |
||||
shape: "https://shaperepo.com/schemas/solidProfile#TypeRegistration", |
||||
context: typeIndexContext, |
||||
}; |
@ -0,0 +1,58 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* Typescript Typings for typeIndex |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* TypeIndexDocument Type |
||||
*/ |
||||
export interface TypeIndexDocument { |
||||
"@id"?: string; |
||||
"@context"?: ContextDefinition; |
||||
/** |
||||
* Defines the node as a TypeIndex | Defines the node as a Listed Document |
||||
*/ |
||||
type: ( |
||||
| { |
||||
"@id": "TypeIndex"; |
||||
} |
||||
| { |
||||
"@id": "ListedDocument"; |
||||
} |
||||
)[]; |
||||
} |
||||
|
||||
/** |
||||
* TypeRegistration Type |
||||
*/ |
||||
export interface TypeRegistration { |
||||
"@id"?: string; |
||||
"@context"?: ContextDefinition; |
||||
/** |
||||
* Defines this node as a Type Registration |
||||
*/ |
||||
type: { |
||||
"@id": "TypeRegistration"; |
||||
}; |
||||
/** |
||||
* The class of object at this type. |
||||
*/ |
||||
forClass: { |
||||
"@id": string; |
||||
}; |
||||
/** |
||||
* A specific resource that contains the class. |
||||
*/ |
||||
instance?: { |
||||
"@id": string; |
||||
}[]; |
||||
/** |
||||
* Containers that contain resources with the class. |
||||
*/ |
||||
instanceContainer?: { |
||||
"@id": string; |
||||
}[]; |
||||
} |
@ -0,0 +1,10 @@ |
||||
PREFIX srs: <https://shaperepo.com/schemas/solidProfile#> |
||||
PREFIX solid: <http://www.w3.org/ns/solid/terms#> |
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||
|
||||
srs:TypeIndexProfile { |
||||
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)" ; |
||||
} |
@ -0,0 +1,23 @@ |
||||
PREFIX srs: <https://shaperepo.com/schemas/solidProfile#> |
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||
PREFIX solid: <http://www.w3.org/ns/solid/terms#> |
||||
PREFIX vcard: <http://www.w3.org/2006/vcard/ns#> |
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||
|
||||
srs:TypeIndexDocument EXTRA a { |
||||
a [ solid:TypeIndex ] |
||||
// rdfs:comment "Defines the node as a TypeIndex" ; |
||||
a [ solid:ListedDocument ] |
||||
// rdfs:comment "Defines the node as a Listed Document" ; |
||||
} |
||||
|
||||
srs:TypeRegistration EXTRA a { |
||||
a [ solid:TypeRegistration ] |
||||
// rdfs:comment "Defines this node as a Type Registration" ; |
||||
solid:forClass IRI |
||||
// rdfs:comment "The class of object at this type." ; |
||||
solid:instance IRI * |
||||
// rdfs:comment "A specific resource that contains the class." ; |
||||
solid:instanceContainer IRI * |
||||
// rdfs:comment "Containers that contain resources with the class." ; |
||||
} |
@ -0,0 +1,32 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* postContext: JSONLD Context for post |
||||
* ============================================================================= |
||||
*/ |
||||
export const postContext: ContextDefinition = { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
SocialMediaPosting: "http://schema.org/SocialMediaPosting", |
||||
CreativeWork: "http://schema.org/CreativeWork", |
||||
Thing: "http://schema.org/Thing", |
||||
articleBody: { |
||||
"@id": "http://schema.org/articleBody", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
uploadDate: { |
||||
"@id": "http://schema.org/uploadDate", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#date", |
||||
}, |
||||
image: { |
||||
"@id": "http://schema.org/image", |
||||
"@type": "@id", |
||||
}, |
||||
publisher: { |
||||
"@id": "http://schema.org/publisher", |
||||
"@type": "@id", |
||||
"@container": "@set", |
||||
}, |
||||
}; |
@ -0,0 +1,155 @@ |
||||
import { Schema } from "shexj"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* postSchema: ShexJ Schema for post |
||||
* ============================================================================= |
||||
*/ |
||||
export const postSchema: Schema = { |
||||
type: "Schema", |
||||
shapes: [ |
||||
{ |
||||
id: "https://example.com/PostSh", |
||||
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/SocialMediaPosting", |
||||
"http://schema.org/CreativeWork", |
||||
"http://schema.org/Thing", |
||||
], |
||||
}, |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/articleBody", |
||||
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#label", |
||||
object: { |
||||
value: "articleBody", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "The actual body of the article. ", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/uploadDate", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#date", |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "uploadDate", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: |
||||
"Date when this media object was uploaded to this site.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/image", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
nodeKind: "iri", |
||||
}, |
||||
min: 0, |
||||
max: 1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "image", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: |
||||
"A media object that encodes this CreativeWork. This property is a synonym for encoding.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/publisher", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
nodeKind: "iri", |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "publisher", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "The publisher of the creative work.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "SocialMediaPost", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: |
||||
"A post to a social media platform, including blog posts, tweets, Facebook posts, etc.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
}, |
||||
], |
||||
}; |
@ -0,0 +1,19 @@ |
||||
import { ShapeType } from "@ldo/ldo"; |
||||
import { postSchema } from "./post.schema"; |
||||
import { postContext } from "./post.context"; |
||||
import { PostSh } from "./post.typings"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* LDO ShapeTypes post |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* PostSh ShapeType |
||||
*/ |
||||
export const PostShShapeType: ShapeType<PostSh> = { |
||||
schema: postSchema, |
||||
shape: "https://example.com/PostSh", |
||||
context: postContext, |
||||
}; |
@ -0,0 +1,45 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* Typescript Typings for post |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* PostSh Type |
||||
*/ |
||||
export interface PostSh { |
||||
"@id"?: string; |
||||
"@context"?: ContextDefinition; |
||||
type: |
||||
| { |
||||
"@id": "SocialMediaPosting"; |
||||
} |
||||
| { |
||||
"@id": "CreativeWork"; |
||||
} |
||||
| { |
||||
"@id": "Thing"; |
||||
}; |
||||
/** |
||||
* The actual body of the article. |
||||
*/ |
||||
articleBody?: string; |
||||
/** |
||||
* Date when this media object was uploaded to this site. |
||||
*/ |
||||
uploadDate: string; |
||||
/** |
||||
* A media object that encodes this CreativeWork. This property is a synonym for encoding. |
||||
*/ |
||||
image?: { |
||||
"@id": string; |
||||
}; |
||||
/** |
||||
* The publisher of the creative work. |
||||
*/ |
||||
publisher: { |
||||
"@id": string; |
||||
}[]; |
||||
} |
@ -0,0 +1,431 @@ |
||||
import React, { useCallback, useEffect, useState } from "react"; |
||||
import type { FunctionComponent } from "react"; |
||||
import { render, screen, fireEvent, act } from "@testing-library/react"; |
||||
import { |
||||
SAMPLE_BINARY_URI, |
||||
SAMPLE_DATA_URI, |
||||
SERVER_DOMAIN, |
||||
setUpServer, |
||||
} from "./setUpServer"; |
||||
import { UnauthenticatedSolidLdoProvider } from "../src/UnauthenticatedSolidLdoProvider"; |
||||
import { useResource } from "../src/useResource"; |
||||
import { useRootContainerFor } from "../src/useRootContainer"; |
||||
import { useLdo } from "../src/SolidLdoProvider"; |
||||
import { PostShShapeType } from "./.ldo/post.shapeTypes"; |
||||
import type { PostSh } from "./.ldo/post.typings"; |
||||
import { useSubject } from "../src/useSubject"; |
||||
|
||||
// Use an increased timeout, since the CSS server takes too much setup time.
|
||||
jest.setTimeout(40_000); |
||||
|
||||
describe("Integration Tests", () => { |
||||
setUpServer(); |
||||
|
||||
/** |
||||
* =========================================================================== |
||||
* useResource |
||||
* =========================================================================== |
||||
*/ |
||||
describe("useResource", () => { |
||||
it("Fetches a resource and indicates it is loading while doing so", async () => { |
||||
const UseResourceTest: FunctionComponent = () => { |
||||
const resource = useResource(SAMPLE_DATA_URI); |
||||
if (resource?.isLoading()) return <p>Loading</p>; |
||||
return <p role="status">{resource.status.type}</p>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseResourceTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
await screen.findByText("Loading"); |
||||
const resourceStatus = await screen.findByRole("status"); |
||||
expect(resourceStatus.innerHTML).toBe("dataReadSuccess"); |
||||
}); |
||||
|
||||
it("returns undefined when no uri is provided, then rerenders when one is", async () => { |
||||
const UseResourceUndefinedTest: FunctionComponent = () => { |
||||
const [uri, setUri] = useState<string | undefined>(undefined); |
||||
const resource = useResource(uri, { suppressInitialRead: true }); |
||||
if (!resource) |
||||
return ( |
||||
<div> |
||||
<p>Undefined</p> |
||||
<button onClick={() => setUri(SAMPLE_DATA_URI)}>Next</button> |
||||
</div> |
||||
); |
||||
return <p role="status">{resource.status.type}</p>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseResourceUndefinedTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
await screen.findByText("Undefined"); |
||||
fireEvent.click(screen.getByText("Next")); |
||||
const resourceStatus = await screen.findByRole("status"); |
||||
expect(resourceStatus.innerHTML).toBe("unfetched"); |
||||
}); |
||||
|
||||
it("Reloads the data on mount", async () => { |
||||
const ReloadTest: FunctionComponent = () => { |
||||
const resource = useResource(SAMPLE_DATA_URI, { reloadOnMount: true }); |
||||
if (resource?.isLoading()) return <p>Loading</p>; |
||||
return <p role="status">{resource.status.type}</p>; |
||||
}; |
||||
const ReloadParent: FunctionComponent = () => { |
||||
const [showComponent, setShowComponent] = useState(true); |
||||
return ( |
||||
<div> |
||||
<button onClick={() => setShowComponent(!showComponent)}> |
||||
Show Component |
||||
</button> |
||||
{showComponent ? <ReloadTest /> : <p>Hidden</p>} |
||||
</div> |
||||
); |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<ReloadParent /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
await screen.findByText("Loading"); |
||||
const resourceStatus1 = await screen.findByRole("status"); |
||||
expect(resourceStatus1.innerHTML).toBe("dataReadSuccess"); |
||||
fireEvent.click(screen.getByText("Show Component")); |
||||
await screen.findByText("Hidden"); |
||||
fireEvent.click(screen.getByText("Show Component")); |
||||
await screen.findByText("Loading"); |
||||
const resourceStatus2 = await screen.findByRole("status", undefined, { |
||||
timeout: 5000, |
||||
}); |
||||
expect(resourceStatus2.innerHTML).toBe("dataReadSuccess"); |
||||
}); |
||||
|
||||
it("handles swapping to a new resource", async () => { |
||||
const SwapResourceTest: FunctionComponent = () => { |
||||
const [uri, setUri] = useState(SAMPLE_DATA_URI); |
||||
const resource = useResource(uri); |
||||
if (resource?.isLoading()) return <p>Loading</p>; |
||||
return ( |
||||
<div> |
||||
<p role="status">{resource.status.type}</p> |
||||
<button onClick={() => setUri(SAMPLE_BINARY_URI)}> |
||||
Update URI |
||||
</button> |
||||
</div> |
||||
); |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<SwapResourceTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
await screen.findByText("Loading"); |
||||
const resourceStatus1 = await screen.findByRole("status"); |
||||
expect(resourceStatus1.innerHTML).toBe("dataReadSuccess"); |
||||
fireEvent.click(screen.getByText("Update URI")); |
||||
await screen.findByText("Loading"); |
||||
const resourceStatus2 = await screen.findByRole("status"); |
||||
expect(resourceStatus2.innerHTML).toBe("binaryReadSuccess"); |
||||
}); |
||||
}); |
||||
|
||||
/** |
||||
* =========================================================================== |
||||
* useRootContainer |
||||
* =========================================================================== |
||||
*/ |
||||
describe("useRootContainer", () => { |
||||
it("gets the root container for a sub-resource", async () => { |
||||
const RootContainerTest: FunctionComponent = () => { |
||||
const rootContainer = useRootContainerFor(SAMPLE_DATA_URI, { |
||||
suppressInitialRead: true, |
||||
}); |
||||
return rootContainer ? <p role="root">{rootContainer?.uri}</p> : <></>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<RootContainerTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
const container = await screen.findByRole("root"); |
||||
expect(container.innerHTML).toBe(SERVER_DOMAIN); |
||||
}); |
||||
|
||||
it("returns undefined when a URI is not provided", async () => { |
||||
const RootContainerTest: FunctionComponent = () => { |
||||
const rootContainer = useRootContainerFor(undefined, { |
||||
suppressInitialRead: true, |
||||
}); |
||||
return rootContainer ? ( |
||||
<p role="root">{rootContainer?.uri}</p> |
||||
) : ( |
||||
<p role="undefined">Undefined</p> |
||||
); |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<RootContainerTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
const container = await screen.findByRole("undefined"); |
||||
expect(container.innerHTML).toBe("Undefined"); |
||||
}); |
||||
}); |
||||
|
||||
/** |
||||
* =========================================================================== |
||||
* useLdoMethods |
||||
* =========================================================================== |
||||
*/ |
||||
describe("useLdoMethods", () => { |
||||
it("uses get subject to get a linked data object", async () => { |
||||
const GetSubjectTest: FunctionComponent = () => { |
||||
const [subject, setSubject] = useState<PostSh | undefined>(); |
||||
const { getSubject } = useLdo(); |
||||
useEffect(() => { |
||||
const someSubject = getSubject( |
||||
PostShShapeType, |
||||
"https://example.com/subject", |
||||
); |
||||
setSubject(someSubject); |
||||
}, []); |
||||
return subject ? <p role="subject">{subject["@id"]}</p> : <></>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<GetSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
const container = await screen.findByRole("subject"); |
||||
expect(container.innerHTML).toBe("https://example.com/subject"); |
||||
}); |
||||
|
||||
it("uses createData to create a new data object", async () => { |
||||
const GetSubjectTest: FunctionComponent = () => { |
||||
const [subject, setSubject] = useState<PostSh | undefined>(); |
||||
const { createData, getResource } = useLdo(); |
||||
useEffect(() => { |
||||
const someSubject = createData( |
||||
PostShShapeType, |
||||
"https://example.com/subject", |
||||
getResource("https://example.com/"), |
||||
); |
||||
someSubject.articleBody = "Cool Article"; |
||||
setSubject(someSubject); |
||||
}, []); |
||||
return subject ? <p role="subject">{subject.articleBody}</p> : <></>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<GetSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
const container = await screen.findByRole("subject"); |
||||
expect(container.innerHTML).toBe("Cool Article"); |
||||
}); |
||||
}); |
||||
|
||||
/** |
||||
* =========================================================================== |
||||
* useSubject |
||||
* =========================================================================== |
||||
*/ |
||||
describe("useSubject", () => { |
||||
it("renders the article body from the useSubject value", async () => { |
||||
const UseSubjectTest: FunctionComponent = () => { |
||||
useResource(SAMPLE_DATA_URI); |
||||
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); |
||||
|
||||
return <p role="article">{post.articleBody}</p>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
|
||||
await screen.findByText("test"); |
||||
}); |
||||
|
||||
it("renders the array value from the useSubject value", async () => { |
||||
const UseSubjectTest: FunctionComponent = () => { |
||||
const resource = useResource(SAMPLE_DATA_URI); |
||||
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); |
||||
if (resource.isLoading() || !post) return <p>loading</p>; |
||||
|
||||
return ( |
||||
<div> |
||||
<p role="single">{post.publisher[0]["@id"]}</p> |
||||
<ul role="list"> |
||||
{post.publisher.map((publisher) => { |
||||
return <li key={publisher["@id"]}>{publisher["@id"]}</li>; |
||||
})} |
||||
</ul> |
||||
</div> |
||||
); |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
|
||||
const single = await screen.findByRole("single"); |
||||
expect(single.innerHTML).toBe("https://example.com/Publisher1"); |
||||
const list = await screen.findByRole("list"); |
||||
expect(list.children[0].innerHTML).toBe("https://example.com/Publisher1"); |
||||
expect(list.children[1].innerHTML).toBe("https://example.com/Publisher2"); |
||||
}); |
||||
|
||||
it("returns undefined in the subject URI is undefined", async () => { |
||||
const UseSubjectTest: FunctionComponent = () => { |
||||
useResource(SAMPLE_DATA_URI, { suppressInitialRead: true }); |
||||
const post = useSubject(PostShShapeType, undefined); |
||||
|
||||
return ( |
||||
<p role="article"> |
||||
{post === undefined ? "Undefined" : "Not Undefined"} |
||||
</p> |
||||
); |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
|
||||
const article = await screen.findByRole("article"); |
||||
expect(article.innerHTML).toBe("Undefined"); |
||||
}); |
||||
|
||||
it("returns nothing if a symbol key is provided", async () => { |
||||
const UseSubjectTest: FunctionComponent = () => { |
||||
const resource = useResource(SAMPLE_DATA_URI); |
||||
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); |
||||
if (resource.isLoading() || !post) return <p>loading</p>; |
||||
|
||||
return <p role="value">{typeof post[Symbol.hasInstance]}</p>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
|
||||
const article = await screen.findByRole("value"); |
||||
expect(article.innerHTML).toBe("undefined"); |
||||
}); |
||||
|
||||
it("returns an id if an id key is provided", async () => { |
||||
const UseSubjectTest: FunctionComponent = () => { |
||||
const resource = useResource(SAMPLE_DATA_URI); |
||||
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); |
||||
if (resource.isLoading() || !post) return <p>loading</p>; |
||||
|
||||
return <p role="value">{post["@id"]}</p>; |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
|
||||
const article = await screen.findByRole("value"); |
||||
expect(article.innerHTML).toBe(`${SAMPLE_DATA_URI}#Post1`); |
||||
}); |
||||
|
||||
it("does not set a value if a value is attempted to be set", async () => { |
||||
const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); |
||||
const UseSubjectTest: FunctionComponent = () => { |
||||
const resource = useResource(SAMPLE_DATA_URI); |
||||
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); |
||||
if (resource.isLoading() || !post) return <p>loading</p>; |
||||
|
||||
return ( |
||||
<div> |
||||
<p role="value">{post.articleBody}</p> |
||||
<button onClick={() => (post.articleBody = "bad")}> |
||||
Attempt Change |
||||
</button> |
||||
</div> |
||||
); |
||||
}; |
||||
render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<UseSubjectTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
|
||||
const article = await screen.findByRole("value"); |
||||
expect(article.innerHTML).toBe(`test`); |
||||
fireEvent.click(screen.getByText("Attempt Change")); |
||||
expect(article.innerHTML).not.toBe("bad"); |
||||
expect(warn).toHaveBeenCalledWith( |
||||
"You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.", |
||||
); |
||||
warn.mockReset(); |
||||
}); |
||||
|
||||
it("rerenders when asked to subscribe to a resource", async () => { |
||||
const NotificationTest: FunctionComponent = () => { |
||||
const resource = useResource(SAMPLE_DATA_URI, { subscribe: true }); |
||||
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); |
||||
|
||||
const addPublisher = useCallback(async () => { |
||||
await fetch(SAMPLE_DATA_URI, { |
||||
method: "PATCH", |
||||
body: `INSERT DATA { <${SAMPLE_DATA_URI}#Post1> <http://schema.org/publisher> <https://example.com/Publisher3> . }`, |
||||
headers: { |
||||
"Content-Type": "application/sparql-update", |
||||
}, |
||||
}); |
||||
}, []); |
||||
|
||||
if (resource.isLoading() || !post) return <p>loading</p>; |
||||
|
||||
return ( |
||||
<div> |
||||
<ul role="list"> |
||||
{post.publisher.map((publisher) => { |
||||
return <li key={publisher["@id"]}>{publisher["@id"]}</li>; |
||||
})} |
||||
</ul> |
||||
<button onClick={addPublisher}>Add Publisher</button> |
||||
</div> |
||||
); |
||||
}; |
||||
const { unmount } = render( |
||||
<UnauthenticatedSolidLdoProvider> |
||||
<NotificationTest /> |
||||
</UnauthenticatedSolidLdoProvider>, |
||||
); |
||||
|
||||
const list = await screen.findByRole("list"); |
||||
expect(list.children[0].innerHTML).toBe("https://example.com/Publisher1"); |
||||
expect(list.children[1].innerHTML).toBe("https://example.com/Publisher2"); |
||||
|
||||
// Wait for subscription to connect
|
||||
await act(async () => { |
||||
await new Promise((resolve) => setTimeout(resolve, 1000)); |
||||
}); |
||||
|
||||
// Click button to add a publisher
|
||||
await fireEvent.click(screen.getByText("Add Publisher")); |
||||
await screen.findByText("https://example.com/Publisher3"); |
||||
|
||||
// Verify the new publisher is in the list
|
||||
const updatedList = await screen.findByRole("list"); |
||||
expect(updatedList.children[2].innerHTML).toBe( |
||||
"https://example.com/Publisher3", |
||||
); |
||||
|
||||
unmount(); |
||||
|
||||
await act(async () => { |
||||
await new Promise((resolve) => setTimeout(resolve, 1000)); |
||||
}); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,110 @@ |
||||
import type { ContainerUri, LeafUri } from "@ldo/solid"; |
||||
import fetch from "cross-fetch"; |
||||
|
||||
export const SERVER_DOMAIN = process.env.SERVER || "http://localhost:3001/"; |
||||
export const ROOT_ROUTE = process.env.ROOT_CONTAINER || "example/"; |
||||
export const ROOT_CONTAINER = `${SERVER_DOMAIN}${ROOT_ROUTE}`; |
||||
export const TEST_CONTAINER_SLUG = "test_ldo/"; |
||||
export const TEST_CONTAINER_URI = |
||||
`${ROOT_CONTAINER}${TEST_CONTAINER_SLUG}` as ContainerUri; |
||||
export const SAMPLE_DATA_URI = `${TEST_CONTAINER_URI}sample.ttl` as LeafUri; |
||||
export const SAMPLE2_DATA_SLUG = "sample2.ttl"; |
||||
export const SAMPLE2_DATA_URI = |
||||
`${TEST_CONTAINER_URI}${SAMPLE2_DATA_SLUG}` as LeafUri; |
||||
export const SAMPLE_BINARY_URI = `${TEST_CONTAINER_URI}sample.txt` as LeafUri; |
||||
export const SAMPLE2_BINARY_SLUG = `sample2.txt`; |
||||
export const SAMPLE2_BINARY_URI = |
||||
`${TEST_CONTAINER_URI}${SAMPLE2_BINARY_SLUG}` as LeafUri; |
||||
export const SAMPLE_CONTAINER_URI = |
||||
`${TEST_CONTAINER_URI}sample_container/` as ContainerUri; |
||||
export const EXAMPLE_POST_TTL = `@prefix schema: <http://schema.org/> .
|
||||
|
||||
<#Post1> |
||||
a schema:CreativeWork, schema:Thing, schema:SocialMediaPosting ; |
||||
schema:image <https://example.com/postImage.jpg> ; |
||||
schema:articleBody "test" ; |
||||
schema:publisher <https://example.com/Publisher1>, <https://example.com/Publisher2> .`;
|
||||
export const TEST_CONTAINER_TTL = `@prefix dc: <http://purl.org/dc/terms/>.
|
||||
@prefix ldp: <http://www.w3.org/ns/ldp#>. |
||||
@prefix posix: <http://www.w3.org/ns/posix/stat#>. |
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>. |
||||
|
||||
<> <urn:npm:solid:community-server:http:slug> "sample.txt"; |
||||
a ldp:Container, ldp:BasicContainer, ldp:Resource; |
||||
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime. |
||||
<sample.ttl> a ldp:Resource, <http://www.w3.org/ns/iana/media-types/text/turtle#Resource>; |
||||
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime. |
||||
<sample.txt> a ldp:Resource, <http://www.w3.org/ns/iana/media-types/text/plain#Resource>; |
||||
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime. |
||||
<> posix:mtime 1697810234; |
||||
ldp:contains <sample.ttl>, <sample.txt>. |
||||
<sample.ttl> posix:mtime 1697810234; |
||||
posix:size 522. |
||||
<sample.txt> posix:mtime 1697810234; |
||||
posix:size 10.`;
|
||||
|
||||
export interface SetUpServerReturn { |
||||
authFetch: typeof fetch; |
||||
fetchMock: jest.Mock< |
||||
Promise<Response>, |
||||
[input: RequestInfo | URL, init?: RequestInit | undefined] |
||||
>; |
||||
} |
||||
|
||||
export function setUpServer(): SetUpServerReturn { |
||||
// Ignore to build s
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const s: SetUpServerReturn = {}; |
||||
|
||||
beforeAll(async () => { |
||||
// s.authFetch = await getAuthenticatedFetch();
|
||||
s.authFetch = fetch; |
||||
}); |
||||
|
||||
beforeEach(async () => { |
||||
s.fetchMock = jest.fn(s.authFetch); |
||||
// Create a new document called sample.ttl
|
||||
await s.authFetch(ROOT_CONTAINER, { |
||||
method: "POST", |
||||
headers: { |
||||
link: '<http://www.w3.org/ns/ldp#Container>; rel="type"', |
||||
slug: TEST_CONTAINER_SLUG, |
||||
}, |
||||
}); |
||||
await Promise.all([ |
||||
s.authFetch(TEST_CONTAINER_URI, { |
||||
method: "POST", |
||||
headers: { "content-type": "text/turtle", slug: "sample.ttl" }, |
||||
body: EXAMPLE_POST_TTL, |
||||
}), |
||||
s.authFetch(TEST_CONTAINER_URI, { |
||||
method: "POST", |
||||
headers: { "content-type": "text/plain", slug: "sample.txt" }, |
||||
body: "some text.", |
||||
}), |
||||
]); |
||||
}); |
||||
|
||||
afterEach(async () => { |
||||
await Promise.all([ |
||||
s.authFetch(SAMPLE_DATA_URI, { |
||||
method: "DELETE", |
||||
}), |
||||
s.authFetch(SAMPLE2_DATA_URI, { |
||||
method: "DELETE", |
||||
}), |
||||
s.authFetch(SAMPLE_BINARY_URI, { |
||||
method: "DELETE", |
||||
}), |
||||
s.authFetch(SAMPLE2_BINARY_URI, { |
||||
method: "DELETE", |
||||
}), |
||||
s.authFetch(SAMPLE_CONTAINER_URI, { |
||||
method: "DELETE", |
||||
}), |
||||
]); |
||||
}); |
||||
|
||||
return s; |
||||
} |
@ -0,0 +1,52 @@ |
||||
{ |
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", |
||||
"import": [ |
||||
"css:config/app/init/initialize-intro.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/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/memory.json", |
||||
"css:config/util/variables/default.json" |
||||
], |
||||
"@graph": [ |
||||
{ |
||||
"comment": "A Solid server that stores its resources in memory and uses WAC for authorization." |
||||
}, |
||||
{ |
||||
"comment": "The location of the new pod templates folder.", |
||||
"@type": "Override", |
||||
"overrideInstance": { |
||||
"@id": "urn:solid-server:default:PodResourcesGenerator" |
||||
}, |
||||
"overrideParameters": { |
||||
"@type": "StaticFolderGenerator", |
||||
"templateFolder": "./test/test-server/configs/template" |
||||
} |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,9 @@ |
||||
[ |
||||
{ |
||||
"email": "hello@example.com", |
||||
"password": "abc123", |
||||
"pods": [ |
||||
{ "name": "example" } |
||||
] |
||||
} |
||||
] |
@ -0,0 +1,13 @@ |
||||
@prefix : <#>. |
||||
@prefix acl: <http://www.w3.org/ns/auth/acl#>. |
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/>. |
||||
@prefix eve: <./>. |
||||
@prefix c: <./profile/card#>. |
||||
|
||||
:ControlReadWrite |
||||
a acl:Authorization; |
||||
acl:accessTo eve:; |
||||
acl:agent c:me, <mailto:info@o.team>; |
||||
acl:agentClass foaf:Agent; |
||||
acl:default eve:; |
||||
acl:mode acl:Control, acl:Read, acl:Write. |
@ -0,0 +1,19 @@ |
||||
# ACL resource for the WebID profile document |
||||
@prefix acl: <http://www.w3.org/ns/auth/acl#>. |
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/>. |
||||
|
||||
# The WebID profile is readable by the public. |
||||
# This is required for discovery and verification, |
||||
# e.g. when checking identity providers. |
||||
<#public> |
||||
a acl:Authorization; |
||||
acl:agentClass foaf:Agent; |
||||
acl:accessTo <./card>; |
||||
acl:mode acl:Read. |
||||
|
||||
# The owner has full access to the profile |
||||
<#owner> |
||||
a acl:Authorization; |
||||
acl:agent <{{webId}}>; |
||||
acl:accessTo <./card>; |
||||
acl:mode acl:Read, acl:Write, acl:Control. |
@ -0,0 +1,7 @@ |
||||
import { createApp } from "./solidServer.helper"; |
||||
|
||||
async function run() { |
||||
const app = await createApp(); |
||||
await app.start(); |
||||
} |
||||
run(); |
@ -0,0 +1,39 @@ |
||||
// 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"; |
||||
|
||||
export async function createApp(): Promise<App> { |
||||
if (process.env.SERVER) { |
||||
return { |
||||
start: () => {}, |
||||
stop: () => {}, |
||||
} as App; |
||||
} |
||||
const appRunner = new AppRunner(); |
||||
|
||||
return appRunner.create({ |
||||
loaderProperties: { |
||||
mainModulePath: resolveModulePath(""), |
||||
typeChecking: false, |
||||
}, |
||||
config: path.join( |
||||
__dirname, |
||||
"configs", |
||||
"components-config", |
||||
"unauthenticatedServer.json", |
||||
), |
||||
variableBindings: {}, |
||||
shorthand: { |
||||
port: 3_001, |
||||
loggingLevel: "off", |
||||
seedConfig: path.join(__dirname, "configs", "solid-css-seed.json"), |
||||
}, |
||||
}); |
||||
} |
||||
|
||||
export interface ISecretData { |
||||
id: string; |
||||
secret: string; |
||||
} |
@ -0,0 +1,8 @@ |
||||
{ |
||||
"extends": "../../tsconfig.base.json", |
||||
"compilerOptions": { |
||||
"outDir": "./dist", |
||||
"lib": ["dom"] |
||||
}, |
||||
"include": ["./src"] |
||||
} |
Loading…
Reference in new issue