Match subject and object are now available as hooks

main
Jackson Morgan 8 months ago
parent 6ce5c27f8b
commit 1f9388871d
  1. 2
      packages/solid-react/src/index.ts
  2. 21
      packages/solid-react/src/useMatchObject.ts
  3. 21
      packages/solid-react/src/useMatchSubject.ts
  4. 59
      packages/solid-react/src/useSubject.ts
  5. 55
      packages/solid-react/src/util/useTrackingProxy.ts
  6. 82
      packages/solid-react/test/Integration.test.tsx
  7. 4
      packages/solid-react/test/setUpServer.ts

@ -7,4 +7,6 @@ export { useLdo } from "./SolidLdoProvider";
// hooks
export * from "./useResource";
export * from "./useSubject";
export * from "./useMatchSubject";
export * from "./useMatchObject";
export * from "./useRootContainer";

@ -0,0 +1,21 @@
import type { LdoBase, ShapeType } from "@ldo/ldo";
import type { QuadMatch } from "@ldo/rdf-utils";
import type { LdoBuilder } from "@ldo/ldo";
import { useCallback } from "react";
import { useTrackingProxy } from "./util/useTrackingProxy";
export function useMatchObject<Type extends LdoBase>(
shapeType: ShapeType<Type>,
subject?: QuadMatch[0] | string,
predicate?: QuadMatch[1] | string,
graph?: QuadMatch[3] | string,
): Type[] {
const matchObject = useCallback(
(builder: LdoBuilder<Type>) => {
return builder.matchObject(subject, predicate, graph);
},
[subject, predicate, graph],
);
return useTrackingProxy(shapeType, matchObject);
}

@ -0,0 +1,21 @@
import type { LdoBase, ShapeType } from "@ldo/ldo";
import type { QuadMatch } from "@ldo/rdf-utils";
import type { LdoBuilder } from "@ldo/ldo";
import { useCallback } from "react";
import { useTrackingProxy } from "./util/useTrackingProxy";
export function useMatchSubject<Type extends LdoBase>(
shapeType: ShapeType<Type>,
predicate?: QuadMatch[1] | string,
object?: QuadMatch[2] | string,
graph?: QuadMatch[3] | string,
): Type[] {
const matchSubject = useCallback(
(builder: LdoBuilder<Type>) => {
return builder.matchSubject(predicate, object, graph);
},
[predicate, object, graph],
);
return useTrackingProxy(shapeType, matchSubject);
}

@ -1,15 +1,10 @@
import type { SubjectNode } from "@ldo/rdf-utils";
import {
ContextUtil,
JsonldDatasetProxyBuilder,
} from "@ldo/jsonld-dataset-proxy";
import type { ShapeType } from "@ldo/ldo";
import { LdoBuilder } from "@ldo/ldo";
import type { LdoBuilder } from "@ldo/ldo";
import type { LdoBase } from "@ldo/ldo";
import { useLdo } from "./SolidLdoProvider";
import { useCallback, useEffect, useMemo, useState } from "react";
import { TrackingProxyContext } from "./util/TrackingProxyContext";
import { defaultGraph } from "@rdfjs/data-model";
import { useCallback } from "react";
import { useTrackingProxy } from "./util/useTrackingProxy";
export function useSubject<Type extends LdoBase>(
shapeType: ShapeType<Type>,
@ -23,45 +18,13 @@ export function useSubject<Type extends LdoBase>(
shapeType: ShapeType<Type>,
subject?: string | SubjectNode,
): Type | undefined {
const { dataset } = useLdo();
const [forceUpdateCounter, setForceUpdateCounter] = useState(0);
const forceUpdate = useCallback(
() => setForceUpdateCounter((val) => val + 1),
[],
const fromSubject = useCallback(
(builder: LdoBuilder<Type>) => {
if (!subject) return;
return builder.fromSubject(subject);
},
[subject],
);
// The main linked data object
const linkedDataObject = useMemo(() => {
if (!subject) return;
// Remove all current subscriptions
dataset.removeListenerFromAllEvents(forceUpdate);
// Rebuild the LdoBuilder from scratch to inject TrackingProxyContext
const contextUtil = new ContextUtil(shapeType.context);
const proxyContext = new TrackingProxyContext(
{
dataset,
contextUtil,
writeGraphs: [defaultGraph()],
languageOrdering: ["none", "en", "other"],
},
forceUpdate,
);
const builder = new LdoBuilder(
new JsonldDatasetProxyBuilder(proxyContext),
shapeType,
);
return builder.fromSubject(subject);
}, [shapeType, subject, dataset, forceUpdateCounter, forceUpdate]);
useEffect(() => {
// Unregister force update listener upon unmount
return () => {
dataset.removeListenerFromAllEvents(forceUpdate);
};
}, [shapeType, subject]);
return linkedDataObject;
return useTrackingProxy(shapeType, fromSubject);
}

@ -0,0 +1,55 @@
import {
ContextUtil,
JsonldDatasetProxyBuilder,
} from "@ldo/jsonld-dataset-proxy";
import { LdoBuilder } from "@ldo/ldo";
import type { LdoBase, ShapeType } from "@ldo/ldo";
import { useCallback, useEffect, useMemo, useState } from "react";
import { TrackingProxyContext } from "./TrackingProxyContext";
import { defaultGraph } from "@rdfjs/data-model";
import { useLdo } from "../SolidLdoProvider";
export function useTrackingProxy<Type extends LdoBase, ReturnType>(
shapeType: ShapeType<Type>,
createLdo: (builder: LdoBuilder<Type>) => ReturnType,
): ReturnType {
const { dataset } = useLdo();
const [forceUpdateCounter, setForceUpdateCounter] = useState(0);
const forceUpdate = useCallback(
() => setForceUpdateCounter((val) => val + 1),
[],
);
// The main linked data object
const linkedDataObject = useMemo(() => {
// Remove all current subscriptions
dataset.removeListenerFromAllEvents(forceUpdate);
// Rebuild the LdoBuilder from scratch to inject TrackingProxyContext
const contextUtil = new ContextUtil(shapeType.context);
const proxyContext = new TrackingProxyContext(
{
dataset,
contextUtil,
writeGraphs: [defaultGraph()],
languageOrdering: ["none", "en", "other"],
},
forceUpdate,
);
const builder = new LdoBuilder(
new JsonldDatasetProxyBuilder(proxyContext),
shapeType,
);
return createLdo(builder);
}, [shapeType, dataset, forceUpdateCounter, forceUpdate, createLdo]);
useEffect(() => {
// Unregister force update listener upon unmount
return () => {
dataset.removeListenerFromAllEvents(forceUpdate);
};
}, [shapeType]);
return linkedDataObject;
}

@ -14,6 +14,8 @@ import { useLdo } from "../src/SolidLdoProvider";
import { PostShShapeType } from "./.ldo/post.shapeTypes";
import type { PostSh } from "./.ldo/post.typings";
import { useSubject } from "../src/useSubject";
import { useMatchSubject } from "../src/useMatchSubject";
import { useMatchObject } from "../src/useMatchObject";
// Use an increased timeout, since the CSS server takes too much setup time.
jest.setTimeout(40_000);
@ -428,4 +430,84 @@ describe("Integration Tests", () => {
});
});
});
/**
* ===========================================================================
* useMatchSubject
* ===========================================================================
*/
describe("useMatchSubject", () => {
it("returns an array of matched subjects", async () => {
const UseMatchSubjectTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI);
const posts = useMatchSubject(
PostShShapeType,
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
"http://schema.org/CreativeWork",
);
if (resource.isLoading()) return <p>loading</p>;
return (
<div>
<ul role="list">
{posts.map((post) => {
return <li key={post["@id"]}>{post["@id"]}</li>;
})}
</ul>
</div>
);
};
render(
<UnauthenticatedSolidLdoProvider>
<UseMatchSubjectTest />
</UnauthenticatedSolidLdoProvider>,
);
const list = await screen.findByRole("list");
expect(list.children[0].innerHTML).toBe(
"http://localhost:3001/example/test_ldo/sample.ttl#Post1",
);
expect(list.children[1].innerHTML).toBe(
"http://localhost:3001/example/test_ldo/sample.ttl#Post2",
);
});
});
/**
* ===========================================================================
* useMatchObject
* ===========================================================================
*/
describe("useMatchObject", () => {
it("returns an array of matched objects", async () => {
const UseMatchObjectTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI);
const publishers = useMatchObject(
PostShShapeType,
"http://localhost:3001/example/test_ldo/sample.ttl#Post1",
"http://schema.org/publisher",
);
if (resource.isLoading()) return <p>loading</p>;
return (
<div>
<ul role="list">
{publishers.map((publisher) => {
return <li key={publisher["@id"]}>{publisher["@id"]}</li>;
})}
</ul>
</div>
);
};
render(
<UnauthenticatedSolidLdoProvider>
<UseMatchObjectTest />
</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");
});
});
});

@ -23,7 +23,9 @@ export const EXAMPLE_POST_TTL = `@prefix schema: <http://schema.org/> .
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> .`;
schema:publisher <https://example.com/Publisher1>, <https://example.com/Publisher2> .
<#Post2>
a schema:CreativeWork, schema:Thing, schema:SocialMediaPosting .`;
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#>.

Loading…
Cancel
Save