From ea28cf28a7025effe8f231a60af943fd34a80767 Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 1 Dec 2023 17:13:44 +0100 Subject: [PATCH] feat: use strapi translations Only merge after https://github.com/digitalservicebund/a2j-rechtsantragstelle-strapi/pull/184 Co-Authored-By: SannyNguyenHung <91742583+SannyNguyenHung@users.noreply.github.com> Co-Authored-By: Joschka Co-Authored-By: Pram Gurusinga <9930966+pgurusinga@users.noreply.github.com> --- app/root.tsx | 2 +- app/routes/beratungshilfe.tsx | 4 +-- ...ilfe.zustaendiges-gericht.auswahl.$PLZ.tsx | 4 +-- app/routes/shared/result.tsx | 4 ++- app/routes/shared/step.tsx | 6 ++-- app/services/cms/getStrapiEntryFromApi.ts | 7 +++-- app/services/cms/getStrapiEntryFromFile.ts | 4 +-- app/services/cms/index.server.ts | 28 ++++++++++++++++--- app/services/cms/models/StrapiFileContent.ts | 6 ++++ app/services/cms/models/StrapiTranslations.ts | 5 ++++ app/services/cms/schemas.ts | 2 ++ .../cms/getStrapiEntryFromApi.test.ts | 2 +- .../cms/getStrapiEntryFromFile.test.ts | 7 +++-- 13 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 app/services/cms/models/StrapiTranslations.ts diff --git a/app/root.tsx b/app/root.tsx index 3717f8554..a8db2a90c 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -99,7 +99,7 @@ export const loader = async ({ request, context }: LoaderFunctionArgs) => { fetchSingleEntry("cookie-banner"), hasTrackingConsent({ request }), getErrorPages(), - fetchMeta({ slug: "/" }), + fetchMeta({ filterValue: "/" }), ]); return json({ diff --git a/app/routes/beratungshilfe.tsx b/app/routes/beratungshilfe.tsx index 797daa20c..2b36349b1 100644 --- a/app/routes/beratungshilfe.tsx +++ b/app/routes/beratungshilfe.tsx @@ -6,8 +6,8 @@ import { throw404IfFeatureFlagEnabled } from "~/services/errorPages/throw404"; export async function loader({ request }: LoaderFunctionArgs) { await throw404IfFeatureFlagEnabled(request); const { pathname } = new URL(request.url); - const slug = `/${pathname.split("/").at(1) ?? ""}`; - return json({ meta: await fetchMeta({ slug }) }); + const filterValue = `/${pathname.split("/").at(1) ?? ""}`; + return json({ meta: await fetchMeta({ filterValue }) }); } export default function View() { diff --git a/app/routes/beratungshilfe.zustaendiges-gericht.auswahl.$PLZ.tsx b/app/routes/beratungshilfe.zustaendiges-gericht.auswahl.$PLZ.tsx index f9c23e029..02e1bda4e 100644 --- a/app/routes/beratungshilfe.zustaendiges-gericht.auswahl.$PLZ.tsx +++ b/app/routes/beratungshilfe.zustaendiges-gericht.auswahl.$PLZ.tsx @@ -24,10 +24,10 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => { // Remove PLZ from slug const { pathname } = new URL(request.url); - const slug = pathname.substring(0, pathname.lastIndexOf("/")); + const filterValue = pathname.substring(0, pathname.lastIndexOf("/")); const [common, meta] = await Promise.all([ fetchSingleEntry("amtsgericht-common"), - fetchMeta({ slug }), + fetchMeta({ filterValue }), ]); const resultListHeading = fillTemplate({ diff --git a/app/routes/shared/result.tsx b/app/routes/shared/result.tsx index f7ea12c67..b35d2d454 100644 --- a/app/routes/shared/result.tsx +++ b/app/routes/shared/result.tsx @@ -63,7 +63,9 @@ export const loader = async ({ const [common, cmsData, parentMeta, amtsgerichtCommon] = await Promise.all([ fetchSingleEntry("vorab-check-common"), fetchCollectionEntry("result-pages", slug), - fetchMeta({ slug: pathname.substring(0, pathname.lastIndexOf("/")) }), + fetchMeta({ + filterValue: pathname.substring(0, pathname.lastIndexOf("/")), + }), fetchSingleEntry("amtsgericht-common"), ]); diff --git a/app/routes/shared/step.tsx b/app/routes/shared/step.tsx index 63e1637c3..241019c36 100644 --- a/app/routes/shared/step.tsx +++ b/app/routes/shared/step.tsx @@ -96,7 +96,9 @@ export const loader = async ({ const [commonContent, formPageContent, parentMeta] = await Promise.all([ fetchSingleEntry("vorab-check-common"), fetchCollectionEntry(currentFlow.cmsSlug, lookupPath), - fetchMeta({ slug: lookupPath.substring(0, lookupPath.lastIndexOf("/")) }), + fetchMeta({ + filterValue: lookupPath.substring(0, lookupPath.lastIndexOf("/")), + }), ]); // To add a inside radio groups, we extract the text from the first

and replace any null labels with it @@ -131,7 +133,7 @@ export const loader = async ({ const navigationLabels = Object.fromEntries( await Promise.all( getSubflowsEntries(currentFlow.flow).map(([subflowName]) => - fetchMeta({ slug: `/${flowId}/${subflowName}` }).then( + fetchMeta({ filterValue: `/${flowId}/${subflowName}` }).then( (meta) => [subflowName, meta.title] as [string, string], ), ), diff --git a/app/services/cms/getStrapiEntryFromApi.ts b/app/services/cms/getStrapiEntryFromApi.ts index a26383775..e07e2b333 100644 --- a/app/services/cms/getStrapiEntryFromApi.ts +++ b/app/services/cms/getStrapiEntryFromApi.ts @@ -7,8 +7,9 @@ import type { StrapiFileContent } from "./models/StrapiFileContent"; const buildUrl = ({ apiId, - slug, pageSize, + filterField = "slug", + filterValue, locale = defaultLocale, populate = "deep", }: GetStrapiEntryOpts) => @@ -18,7 +19,9 @@ const buildUrl = ({ `?populate=${populate}`, `&locale=${locale}`, pageSize ? `&pagination[pageSize]=${pageSize}` : "", - slug ? `&filters[slug][$eq]=${slug}` : "", + filterField && filterValue + ? `&filters[${filterField}][$eq]=${filterValue}` + : "", ].join(""); type SingleStrapiEntry = diff --git a/app/services/cms/getStrapiEntryFromFile.ts b/app/services/cms/getStrapiEntryFromFile.ts index 0c59eb85d..be60b7074 100644 --- a/app/services/cms/getStrapiEntryFromFile.ts +++ b/app/services/cms/getStrapiEntryFromFile.ts @@ -30,9 +30,9 @@ export const getStrapiEntryFromFile = async ({ return false; return !( - opts.slug && + opts.filterValue && "slug" in item.attributes && - item.attributes.slug !== opts.slug + item.attributes.slug !== opts.filterValue ); })?.attributes; }; diff --git a/app/services/cms/index.server.ts b/app/services/cms/index.server.ts index df9285279..cc62fc0af 100644 --- a/app/services/cms/index.server.ts +++ b/app/services/cms/index.server.ts @@ -10,7 +10,8 @@ import { collectionSchemas, entrySchemas } from "./schemas"; export type GetStrapiEntryOpts = { apiId: keyof StrapiFileContent; - slug?: string; + filterField?: string; + filterValue?: string; locale?: StrapiLocale; populate?: string; pageSize?: string; @@ -37,18 +38,37 @@ export async function fetchCollectionEntry< ApiId extends keyof CollectionSchemas, >( apiId: ApiId, - slug: string, + filterValue: string, + filterField = "slug", locale?: StrapiLocale, ): Promise> { - const strapiEntry = await getStrapiEntry({ apiId, locale, slug }); + const strapiEntry = await getStrapiEntry({ + apiId, + locale, + filterValue, + filterField, + }); + if (!strapiEntry) { - const error = new Error(`page missing in cms: ${slug}`); + const error = new Error( + `page missing in cms: ${filterField}:${filterValue}`, + ); error.name = "StrapiPageNotFound"; throw error; } return collectionSchemas[apiId].parse(strapiEntry); } +export const strapiTranslation = async ( + name: string, + locale?: StrapiLocale, +) => { + const entry = fetchCollectionEntry("translations", name, "scope", locale); + return Object.fromEntries( + (await entry).field.map(({ name, value }) => [name, value]), + ); +}; + export const strapiPageFromRequest = async ({ request, locale, diff --git a/app/services/cms/models/StrapiFileContent.ts b/app/services/cms/models/StrapiFileContent.ts index a9670bf0a..04606de47 100644 --- a/app/services/cms/models/StrapiFileContent.ts +++ b/app/services/cms/models/StrapiFileContent.ts @@ -10,6 +10,7 @@ import { StrapiCookieBannerSchema } from "./StrapiCookieBannerSchema"; import { StrapiPageHeaderSchema } from "./StrapiPageHeader"; import { StrapiGlobalSchema } from "./StrapiGlobal"; import { StrapiFormFlowPageSchema } from "./StrapiFormFlowPage"; +import { StrapiTranslationSchema } from "./StrapiTranslations"; export const StrapiFileContentSchema = z.object({ "amtsgericht-common": z.array( @@ -62,6 +63,11 @@ export const StrapiFileContentSchema = z.object({ attributes: StrapiFormFlowPageSchema, }), ), + translations: z.array( + HasStrapiIdSchema.extend({ + attributes: StrapiTranslationSchema, + }), + ), }); export type StrapiFileContent = z.infer; diff --git a/app/services/cms/models/StrapiTranslations.ts b/app/services/cms/models/StrapiTranslations.ts new file mode 100644 index 000000000..77db65700 --- /dev/null +++ b/app/services/cms/models/StrapiTranslations.ts @@ -0,0 +1,5 @@ +import { z } from "zod"; + +export const StrapiTranslationSchema = z.object({ + field: z.array(z.object({ name: z.string(), value: z.string() })), +}); diff --git a/app/services/cms/schemas.ts b/app/services/cms/schemas.ts index e49987342..2e4ff97ec 100644 --- a/app/services/cms/schemas.ts +++ b/app/services/cms/schemas.ts @@ -8,6 +8,7 @@ import { StrapiCookieBannerSchema } from "./models/StrapiCookieBannerSchema"; import { StrapiPageHeaderSchema } from "./models/StrapiPageHeader"; import { StrapiGlobalSchema } from "./models/StrapiGlobal"; import { StrapiFormFlowPageSchema } from "./models/StrapiFormFlowPage"; +import { StrapiTranslationSchema } from "./models/StrapiTranslations"; export const entrySchemas = { "page-header": StrapiPageHeaderSchema, @@ -24,5 +25,6 @@ export const collectionSchemas = { "result-pages": StrapiResultPageSchema, "vorab-check-pages": StrapiVorabCheckPageSchema, "form-flow-pages": StrapiFormFlowPageSchema, + translations: StrapiTranslationSchema, } as const; export type CollectionSchemas = typeof collectionSchemas; diff --git a/tests/unit/services/cms/getStrapiEntryFromApi.test.ts b/tests/unit/services/cms/getStrapiEntryFromApi.test.ts index 6aa5a4992..6c2c6c22b 100644 --- a/tests/unit/services/cms/getStrapiEntryFromApi.test.ts +++ b/tests/unit/services/cms/getStrapiEntryFromApi.test.ts @@ -47,7 +47,7 @@ describe("services/cms", () => { mockedAxios.get .mockResolvedValue(defaultResponseData) .mockResolvedValueOnce(emptyResponseData); - await getStrapiEntryFromApi({ ...defaultOptions, slug: "foobar" }); + await getStrapiEntryFromApi({ ...defaultOptions, filterValue: "foobar" }); expect(axiosGetSpy).toHaveBeenNthCalledWith( 1, `${expectedStagingRequestUrl}&filters[slug][$eq]=foobar`, diff --git a/tests/unit/services/cms/getStrapiEntryFromFile.test.ts b/tests/unit/services/cms/getStrapiEntryFromFile.test.ts index 3cc2169f9..14751f2c1 100644 --- a/tests/unit/services/cms/getStrapiEntryFromFile.test.ts +++ b/tests/unit/services/cms/getStrapiEntryFromFile.test.ts @@ -32,6 +32,7 @@ describe("services/cms", () => { "vorab-check-common": [], "vorab-check-pages": [], "form-flow-pages": [], + translations: [], } satisfies StrapiFileContent; (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(fileContent)); @@ -61,7 +62,7 @@ describe("services/cms", () => { expect( await getStrapiEntryFromFile({ apiId: "pages", - slug: "/impressum", + filterValue: "/impressum", locale: "de", }), ).toEqual(impressum); @@ -72,7 +73,7 @@ describe("services/cms", () => { expect( await getStrapiEntryFromFile({ apiId: "pages", - slug: "/NOTAVAILABLE", + filterValue: "/NOTAVAILABLE", locale: "de", }), ).toBeUndefined(); @@ -84,7 +85,7 @@ describe("services/cms", () => { expect( await getStrapiEntryFromFile({ apiId: "pages", - slug: "/impressum", + filterValue: "/impressum", locale: "en", }), ).toBeUndefined();