-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Models): refresh Models by tags (URLs per default)
- Loading branch information
Showing
21 changed files
with
1,632 additions
and
81 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
File renamed without changes.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { FC, PropsWithChildren, ReactNode } from "react"; | ||
import { usePromise } from "@mittwald/react-use-promise"; | ||
import { setModule } from "./reactUsePromise.js"; | ||
|
||
interface Props extends PropsWithChildren { | ||
fallback?: ReactNode; | ||
} | ||
|
||
export const MittwaldApiModelProvider: FC<Props> = (props) => { | ||
const { fallback, children } = props; | ||
|
||
const module = usePromise( | ||
() => import("@mittwald/react-use-promise").then(setModule), | ||
[], | ||
{ | ||
useSuspense: false, | ||
}, | ||
); | ||
|
||
if (!module.hasValue) { | ||
return fallback; | ||
} | ||
|
||
return children; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Commons } from "@mittwald/api-client"; | ||
import { reactProvisionContext } from "./reactProvisionContext.js"; | ||
import { refresh } from "@mittwald/react-use-promise"; | ||
import { Store } from "@mittwald/react-use-promise/store"; | ||
|
||
const cacheTagStore = new Store<Set<number>>(); | ||
|
||
export const refreshModels = (tag: string) => { | ||
cacheTagStore.getAll(tag).forEach((ids) => { | ||
ids.forEach((id) => { | ||
refresh({ | ||
tag: String(id), | ||
}); | ||
}); | ||
}); | ||
}; | ||
|
||
export const addCacheTag = (tag: string) => { | ||
const context = reactProvisionContext.use(); | ||
|
||
if (context) { | ||
const ids = cacheTagStore.get(tag) ?? new Set<number>(); | ||
ids.add(context.id); | ||
|
||
cacheTagStore.set(tag, () => ids, { | ||
tags: [tag], | ||
}); | ||
} | ||
}; | ||
|
||
export const updateCacheTagsBeforeRequest: Commons.RequestOptions["onBeforeRequest"] = | ||
(request) => { | ||
const url = request.requestConfig.url; | ||
|
||
if (request.requestConfig.method === "GET" && url) { | ||
addCacheTag(url); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/** @jest-environment jsdom */ | ||
|
||
import "@testing-library/jest-dom"; | ||
import { render, screen } from "@testing-library/react"; | ||
import { ReferenceModel } from "../base/index.js"; | ||
import { provideReact } from "./provideReact.js"; | ||
import React, { act, FC, PropsWithChildren, Suspense } from "react"; | ||
import { beforeEach, jest } from "@jest/globals"; | ||
import { MittwaldApiModelProvider } from "./MittwaldApiModelProvider.js"; | ||
import { addCacheTag, refreshModels } from "./asyncResourceInvalidation.js"; | ||
import { asyncResourceStore } from "@mittwald/react-use-promise"; | ||
|
||
const simulatedDataLoad = jest.fn(); | ||
let rerender: ReturnType<typeof render>["rerender"] | undefined; | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
jest.useFakeTimers(); | ||
asyncResourceStore.clear(); | ||
rerender = undefined; | ||
|
||
simulatedDataLoad.mockImplementation(() => { | ||
return new Promise((res) => setTimeout(res, 100)); | ||
}); | ||
}); | ||
|
||
class TestModel extends ReferenceModel { | ||
public static ofId(id: number) { | ||
return new TestModel(String(id)); | ||
} | ||
|
||
public getDetailed = provideReact(async () => { | ||
addCacheTag(`test/get/${this.id}`); | ||
await simulatedDataLoad(); | ||
return { | ||
id: this.id, | ||
foo: true, | ||
}; | ||
}, [this.id]); | ||
} | ||
|
||
const TestComponent: FC<{ id: number }> = (props) => { | ||
const model = TestModel.ofId(props.id).getDetailed.use(); | ||
return <span>{model.id}</span>; | ||
}; | ||
|
||
const TestWrapper: FC<PropsWithChildren> = (props) => ( | ||
<MittwaldApiModelProvider> | ||
<Suspense>{props.children}</Suspense> | ||
</MittwaldApiModelProvider> | ||
); | ||
|
||
const runTest = async (id: number, expectedDataLoadingCount: number) => { | ||
const ui = rerender | ||
? rerender(<TestComponent id={id} />) | ||
: render(<TestComponent id={id} />, { | ||
wrapper: TestWrapper, | ||
}); | ||
|
||
if (ui) { | ||
rerender = ui.rerender; | ||
} | ||
expect(await screen.findByText(id)).toBeInTheDocument(); | ||
expect(simulatedDataLoad).toHaveBeenCalledTimes(expectedDataLoadingCount); | ||
}; | ||
|
||
test("Model data can be used", async () => { | ||
await runTest(42, 1); | ||
await runTest(43, 2); | ||
}); | ||
|
||
test("Model caches data", async () => { | ||
await runTest(42, 1); | ||
await runTest(43, 2); | ||
await runTest(42, 2); | ||
await runTest(43, 2); | ||
}); | ||
|
||
test("Model cache can be refreshed", async () => { | ||
await runTest(42, 1); | ||
// Tag does not exist | ||
act(() => refreshModels("foo")); | ||
await runTest(42, 1); | ||
// Tag exist | ||
act(() => refreshModels("test/get/42")); | ||
await runTest(42, 2); | ||
// Tag exist | ||
act(() => refreshModels("test/**/*")); | ||
await runTest(42, 3); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { createCascade } from "context"; | ||
|
||
export const reactProvisionContext = createCascade<{ | ||
id: number; | ||
}>(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.