From dcd63ac15ab14b8b9eb39359daafbd8876d65a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Lauk=C3=B6tter?= Date: Wed, 6 Mar 2024 09:18:27 +0100 Subject: [PATCH] Load target law data RISDEV-3463 --- frontend/src/composables/useTargetLaw.spec.ts | 80 +++++++++++++++++++ frontend/src/composables/useTargetLaw.ts | 33 ++++++++ .../src/services/targetLawsService.spec.ts | 66 +++++++++++++++ frontend/src/services/targetLawsService.ts | 28 +++++++ frontend/src/store/targetLawStore.spec.ts | 48 +++++++++++ frontend/src/store/targetLawStore.ts | 35 ++++++++ .../src/views/AmendingLawArticleEditor.vue | 7 +- 7 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 frontend/src/composables/useTargetLaw.spec.ts create mode 100644 frontend/src/composables/useTargetLaw.ts create mode 100644 frontend/src/services/targetLawsService.spec.ts create mode 100644 frontend/src/services/targetLawsService.ts create mode 100644 frontend/src/store/targetLawStore.spec.ts create mode 100644 frontend/src/store/targetLawStore.ts diff --git a/frontend/src/composables/useTargetLaw.spec.ts b/frontend/src/composables/useTargetLaw.spec.ts new file mode 100644 index 000000000..956ded820 --- /dev/null +++ b/frontend/src/composables/useTargetLaw.spec.ts @@ -0,0 +1,80 @@ +import { defineStore } from "pinia" +import { beforeEach, describe, expect, test, vi } from "vitest" +import { nextTick, ref } from "vue" +import { createTestingPinia } from "@pinia/testing" + +describe("useTargetLaw", () => { + beforeEach(() => { + vi.resetModules() + vi.resetAllMocks() + }) + + test("should provide the target law", async () => { + createTestingPinia({ + stubActions: false, + }) + + vi.doMock("@/store/targetLawStore", () => ({ + useTargetLawStore: vi.fn().mockReturnValue( + defineStore("target-law", () => { + return { + targetLaw: ref({ + eli: "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + title: + "Gesetz über die Zusammenarbeit des Bundes und der Länder in Angelegenheiten des Verfassungsschutzes und über das Bundesamt für Verfassungsschutz", + }), + loadTargetLawByEli: vi.fn(), + } + })(), + ), + })) + + const { useTargetLaw } = await import("./useTargetLaw") + + const eli = ref( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + const targetLaw = useTargetLaw(eli) + + expect(targetLaw.value?.eli).toBe( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + expect(targetLaw.value?.title).toBe( + "Gesetz über die Zusammenarbeit des Bundes und der Länder in Angelegenheiten des Verfassungsschutzes und über das Bundesamt für Verfassungsschutz", + ) + }) + + test("should load the target law when the eli changes", async () => { + const loadTargetLawByEli = vi.fn() + + createTestingPinia({ + stubActions: false, + }) + + vi.doMock("@/store/targetLawStore", () => ({ + useTargetLawStore: vi.fn().mockReturnValue( + defineStore("target-law", () => { + return { + targetLaw: ref(), + loadTargetLawByEli, + } + })(), + ), + })) + + const { useTargetLaw } = await import("./useTargetLaw") + + const eli = ref( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + useTargetLaw(eli) + + eli.value = "eli/bund/bgbl-1/1964/s593/1964-08-05/1/deu/regelungstext-1" + await nextTick() + + eli.value = "eli/bund/bgbl-1/1964/s593/1964-08-05/1/deu/regelungstext-1" + await nextTick() + + expect(loadTargetLawByEli).toBeCalledTimes(2) + }) +}) diff --git a/frontend/src/composables/useTargetLaw.ts b/frontend/src/composables/useTargetLaw.ts new file mode 100644 index 000000000..d39efaaa2 --- /dev/null +++ b/frontend/src/composables/useTargetLaw.ts @@ -0,0 +1,33 @@ +import { storeToRefs } from "pinia" +import { + readonly, + watch, + MaybeRefOrGetter, + toValue, + Ref, + DeepReadonly, +} from "vue" +import { useTargetLawStore } from "@/store/targetLawStore" +import { TargetLaw } from "@/services/targetLawsService" + +/** + * Get the data of a target law. + * @param eli a reference to the eli for which the law data will be returned. Changing the value of the reference will load the data for the new eli. + * @returns A reference to the target law or undefined if it is not available (or still loading). + */ +export function useTargetLaw( + eli: MaybeRefOrGetter, +): DeepReadonly> { + const targetLawStore = useTargetLawStore() + const { targetLaw } = storeToRefs(targetLawStore) + + watch( + () => toValue(eli), + (newEli) => { + targetLawStore.loadTargetLawByEli(newEli) + }, + { immediate: true }, + ) + + return readonly(targetLaw) +} diff --git a/frontend/src/services/targetLawsService.spec.ts b/frontend/src/services/targetLawsService.spec.ts new file mode 100644 index 000000000..c7abb8800 --- /dev/null +++ b/frontend/src/services/targetLawsService.spec.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" + +describe("targetLawsService", () => { + beforeEach(() => { + vi.resetModules() + vi.resetAllMocks() + }) + + describe("getTargetLawByEli(eli)", () => { + it("provides the data from the api", async () => { + const fetchMock = vi.fn().mockResolvedValueOnce({ + eli: "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + title: + "Gesetz über die Zusammenarbeit des Bundes und der Länder in Angelegenheiten des Verfassungsschutzes und über das Bundesamt für Verfassungsschutz", + }) + + vi.doMock("./apiService.ts", () => ({ + apiFetch: fetchMock, + })) + + const { getTargetLawByEli } = await import("./targetLawsService") + + const result = await getTargetLawByEli( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + expect(result.eli).toBe( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + expect(result.title).toBe( + "Gesetz über die Zusammenarbeit des Bundes und der Länder in Angelegenheiten des Verfassungsschutzes und über das Bundesamt für Verfassungsschutz", + ) + + expect(fetchMock).toHaveBeenCalledWith( + "/target-laws/eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + }) + }) + + describe("getTargetLawXmlByEli(eli)", () => { + it("provides the data from the api", async () => { + const fetchMock = vi + .fn() + .mockResolvedValueOnce(``) + + vi.doMock("./apiService.ts", () => ({ + apiFetch: fetchMock, + })) + + const { getTargetLawXmlByEli } = await import("./targetLawsService") + + const result = await getTargetLawXmlByEli( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + expect(result).toBe('') + + expect(fetchMock).toHaveBeenCalledWith( + "/target-laws/eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + expect.objectContaining({ + headers: expect.objectContaining({ + Accept: "application/xml", + }), + }), + ) + }) + }) +}) diff --git a/frontend/src/services/targetLawsService.ts b/frontend/src/services/targetLawsService.ts new file mode 100644 index 000000000..9070f72da --- /dev/null +++ b/frontend/src/services/targetLawsService.ts @@ -0,0 +1,28 @@ +import { apiFetch } from "@/services/apiService" + +export interface TargetLaw { + eli: string + title: string +} + +/** + * Load a target law from the api + * + * @param eli Eli of the target law + */ +export async function getTargetLawByEli(eli: string): Promise { + return await apiFetch(`/target-laws/${eli}`) +} + +/** + * Load the xml version of a target law from the api + * + * @param eli Eli of the target law + */ +export async function getTargetLawXmlByEli(eli: string): Promise { + return await apiFetch(`/target-laws/${eli}`, { + headers: { + Accept: "application/xml", + }, + }) +} diff --git a/frontend/src/store/targetLawStore.spec.ts b/frontend/src/store/targetLawStore.spec.ts new file mode 100644 index 000000000..486b5c17f --- /dev/null +++ b/frontend/src/store/targetLawStore.spec.ts @@ -0,0 +1,48 @@ +import { describe, it, expect, beforeEach, vi } from "vitest" +import { setActivePinia, createPinia } from "pinia" +import { nextTick } from "vue" + +describe("useTargetLawsStore", () => { + beforeEach(() => { + vi.resetModules() + vi.resetAllMocks() + }) + + beforeEach(() => { + setActivePinia(createPinia()) + }) + + it("loads amending laws correctly", async () => { + const getTargetLawByEli = vi.fn().mockResolvedValue({ + eli: "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + title: + "Gesetz über die Zusammenarbeit des Bundes und der Länder in Angelegenheiten des Verfassungsschutzes und über das Bundesamt für Verfassungsschutz", + }) + + vi.doMock("@/services/targetLawsService", () => ({ + getTargetLawByEli, + })) + + const { useTargetLawStore } = await import("./targetLawStore") + + const store = useTargetLawStore() + + store.loadTargetLawByEli( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + await nextTick() + + expect(store.loading).toBe(true) + expect(getTargetLawByEli).toHaveBeenCalledOnce() + + await vi.waitUntil(() => !store.loading) + + expect(store.loading).toBe(false) + expect(store.targetLaw?.eli).toEqual( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", + ) + expect(store.targetLaw?.title).toEqual( + "Gesetz über die Zusammenarbeit des Bundes und der Länder in Angelegenheiten des Verfassungsschutzes und über das Bundesamt für Verfassungsschutz", + ) + }) +}) diff --git a/frontend/src/store/targetLawStore.ts b/frontend/src/store/targetLawStore.ts new file mode 100644 index 000000000..9cb830c34 --- /dev/null +++ b/frontend/src/store/targetLawStore.ts @@ -0,0 +1,35 @@ +import { defineStore } from "pinia" +import { ref, watch } from "vue" +import { getTargetLawByEli, TargetLaw } from "@/services/targetLawsService" + +export const useTargetLawStore = defineStore("target-law", () => { + const targetLaw = ref(undefined) + const eli = ref(undefined) + const loading = ref(false) + + function loadTargetLawByEli(newEli: string): void { + eli.value = newEli + } + + watch(eli, async (newEli) => { + if (newEli) { + if (loading.value) { + // todo (Malte Laukötter, 2024-03-06): We should cancel a possibly still running call of getTargetLawByEli here. The AbortController-API should allow us to do this. + console.warn( + "There is already an unfinished call to getTargetLawByEli, we are still creating a new one but this could have created a race condition.", + ) + } + loading.value = true + targetLaw.value = undefined + targetLaw.value = await getTargetLawByEli(newEli) + loading.value = false + } + }) + + return { + targetLaw, + eli, + loading, + loadTargetLawByEli, + } +}) diff --git a/frontend/src/views/AmendingLawArticleEditor.vue b/frontend/src/views/AmendingLawArticleEditor.vue index 3fa886936..571717656 100644 --- a/frontend/src/views/AmendingLawArticleEditor.vue +++ b/frontend/src/views/AmendingLawArticleEditor.vue @@ -6,8 +6,8 @@ import IconArrowBack from "~icons/ic/baseline-arrow-back" import { useAmendingLaw } from "@/composables/useAmendingLaw" import RisAmendingLawInfoHeader from "@/components/amendingLaws/RisAmendingLawInfoHeader.vue" import { getAmendingLawXmlByEli } from "@/services/amendingLawsService" +import { useTargetLaw } from "@/composables/useTargetLaw" -const TARGET_LAW_TITLE = "Bundesverfassungsschutzgesetz" const TARGET_LAW_XML = ` amendingLaw.value?.articles?.[0]) +const targetLaw = useTargetLaw( + "eli/bund/bgbl-1/1990/s2954/2022-12-19/1/deu/regelungstext-1", +) const articleXml = ref("") /** @@ -93,7 +96,7 @@ watch(
-

{{ TARGET_LAW_TITLE }}

+

{{ targetLaw?.title }}