UNPKG

graph-explorer

Version:

Graph Explorer can be used to explore and RDF graphs in SPARQL endpoints or on the web.

230 lines (208 loc) 6.36 kB
import { ElementModel, LinkModel, ElementIri, ElementTypeIri, LinkTypeIri, PropertyTypeIri, LinkDirection, MetadataApi, ValidationApi, ValidationEvent, ElementError, LinkError, LiteralProperty, DirectedLinkType, CancellationToken, } from "../../src/graph-explorer/index"; const OWL_PREFIX = "http://www.w3.org/2002/07/owl#"; const RDFS_PREFIX = "http://www.w3.org/2000/01/rdf-schema#"; const owl = { class: (OWL_PREFIX + "Class") as ElementTypeIri, objectProperty: (OWL_PREFIX + "ObjectProperty") as ElementTypeIri, domain: (OWL_PREFIX + "domain") as LinkTypeIri, range: (OWL_PREFIX + "range") as LinkTypeIri, }; const rdfs = { subClassOf: (RDFS_PREFIX + "subClassOf") as LinkTypeIri, subPropertyOf: (RDFS_PREFIX + "subPropertyOf") as LinkTypeIri, }; function hasType(model: ElementModel, type: ElementTypeIri) { return Boolean(model.types.find((t) => t === type)); } const SIMULATED_DELAY = 500; /* ms */ export class ExampleMetadataApi implements MetadataApi { async canDropOnCanvas( source: ElementModel, ct: CancellationToken ): Promise<boolean> { await delay(SIMULATED_DELAY, ct); const elementTypes = await this.typesOfElementsDraggedFrom(source, ct); CancellationToken.throwIfAborted(ct); return elementTypes.length > 0; } async canDropOnElement( source: ElementModel, target: ElementModel, ct: CancellationToken ): Promise<boolean> { //await delay(SIMULATED_DELAY, ct); const linkTypes = await this.possibleLinkTypes(source, target, ct); CancellationToken.throwIfAborted(ct); return linkTypes.length > 0; } async possibleLinkTypes( source: ElementModel, target: ElementModel, ct: CancellationToken ): Promise<DirectedLinkType[]> { await delay(SIMULATED_DELAY, ct); return hasType(source, owl.class) && hasType(target, owl.class) ? mapLinkTypes([rdfs.subClassOf]).concat( mapLinkTypes([rdfs.subClassOf], LinkDirection.in) ) : hasType(source, owl.objectProperty) && hasType(target, owl.class) ? mapLinkTypes([owl.domain, owl.range]) : hasType(target, owl.objectProperty) && hasType(source, owl.class) ? mapLinkTypes([owl.domain, owl.range], LinkDirection.in) : hasType(source, owl.objectProperty) && hasType(target, owl.objectProperty) ? mapLinkTypes([rdfs.subPropertyOf]).concat( mapLinkTypes([rdfs.subPropertyOf], LinkDirection.in) ) : []; function mapLinkTypes( types: LinkTypeIri[], direction: LinkDirection = LinkDirection.out ): DirectedLinkType[] { return types.map((linkTypeIri) => ({ linkTypeIri, direction })); } } async typesOfElementsDraggedFrom( source: ElementModel, ct: CancellationToken ): Promise<ElementTypeIri[]> { await delay(SIMULATED_DELAY, ct); return hasType(source, owl.class) ? [owl.class] : hasType(source, owl.objectProperty) ? [owl.class, owl.objectProperty] : []; } async propertiesForType( _type: ElementTypeIri, _ct: CancellationToken ): Promise<PropertyTypeIri[]> { //await delay(SIMULATED_DELAY, ct); return []; } async canDeleteElement( element: ElementModel, ct: CancellationToken ): Promise<boolean> { await delay(SIMULATED_DELAY, ct); return true; } async filterConstructibleTypes( types: ReadonlySet<ElementTypeIri>, _ct: CancellationToken ): Promise<ReadonlySet<ElementTypeIri>> { //await delay(SIMULATED_DELAY, ct); const result = new Set<ElementTypeIri>(); types.forEach((type) => { if (type === "http://xmlns.com/foaf/0.1/Person") { result.add(type); } }); return result; } async canEditElement( element: ElementModel, ct: CancellationToken ): Promise<boolean> { await delay(SIMULATED_DELAY, ct); return true; } async canLinkElement( _element: ElementModel, _ct: CancellationToken ): Promise<boolean> { //await delay(SIMULATED_DELAY, ct); return true; } async canDeleteLink( _link: LinkModel, _source: ElementModel, _target: ElementModel, _ct: CancellationToken ): Promise<boolean> { //await delay(SIMULATED_DELAY, ct); return true; } async canEditLink( _link: LinkModel, _source: ElementModel, _target: ElementModel, _ct: CancellationToken ): Promise<boolean> { //await delay(SIMULATED_DELAY, ct); return true; } async generateNewElement( types: readonly ElementTypeIri[], ct: CancellationToken ): Promise<ElementModel> { await delay(SIMULATED_DELAY, ct); const random32BitDigits = Math.floor((1 + Math.random()) * 0x100000000) .toString(16) .substring(1); return { id: `${types[0]}_${random32BitDigits}` as ElementIri, types: [...types], label: { values: [{ value: "New Entity", language: "" }] }, properties: { "http://xmlns.com/foaf/0.1/name": { type: "string", values: [{ language: "", value: "helo" }], } as LiteralProperty, }, }; } } export class ExampleValidationApi implements ValidationApi { async validate( event: ValidationEvent ): Promise<(ElementError | LinkError)[]> { const errors: (ElementError | LinkError)[] = []; if (event.target.types.indexOf(owl.class) >= 0) { event.state.links.forEach((e) => { if (!e.before && e.after.sourceId === event.target.id) { errors.push({ type: "link", target: e.after, message: "Cannot add any new link from a Class", }); const linkType = event.model.createLinkType(e.after.linkTypeId); errors.push({ type: "element", target: event.target.id, message: `Cannot create <${linkType.id}> link from a Class`, }); } }); } await delay(SIMULATED_DELAY, event.cancellation); return errors; } } async function delay(amountMs: number, ct: CancellationToken) { CancellationToken.throwIfAborted(ct); await waitTimeout(amountMs); CancellationToken.throwIfAborted(ct); } function waitTimeout(amountMs: number): Promise<void> { if (amountMs === 0) { return Promise.resolve(); } return new Promise((resolve) => setTimeout(resolve, amountMs)); }