Merge pull request #68 from o-development/feat/match-subject-object

Match subject and object are now available as hooks
main
jaxoncreed 10 months ago committed by GitHub
commit a9fd37c325
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  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