Merged in feat/revalidate-cache-my-pages-breadcrumbs (pull request #135)

Feat/revalidate cache my pages breadcrumbs

Approved-by: Michael Zetterberg
This commit is contained in:
Simon.Emanuelsson
2024-05-06 13:21:19 +00:00
committed by Michael Zetterberg
24 changed files with 322 additions and 57 deletions

View File

@@ -28,7 +28,7 @@ export default async function CurrentContentPage({
url: searchParams.uri, url: searchParams.uri,
}, },
{ {
tags: [`${searchParams.uri}-${params.lang}`], next: { tags: [`${searchParams.uri}-${params.lang}`] },
} }
) )
@@ -44,7 +44,7 @@ export default async function CurrentContentPage({
GetCurrentBlockPageTrackingData, GetCurrentBlockPageTrackingData,
{ uid: response.data.all_current_blocks_page.items[0].system.uid }, { uid: response.data.all_current_blocks_page.items[0].system.uid },
{ {
tags: [`${searchParams.uri}-en`], next: { tags: [`${searchParams.uri}-en`] },
} }
) )

View File

@@ -6,6 +6,7 @@ import { z } from "zod"
import { Lang } from "@/constants/languages" import { Lang } from "@/constants/languages"
import { env } from "@/env/server" import { env } from "@/env/server"
import { internalServerError } from "@/server/errors/next" import { internalServerError } from "@/server/errors/next"
import { affix as breadcrumbsAffix } from "@/server/routers/contentstack/breadcrumbs/utils"
import { import {
generateRefsResponseTag, generateRefsResponseTag,
@@ -14,14 +15,16 @@ import {
} from "@/utils/generateTag" } from "@/utils/generateTag"
const validateJsonBody = z.object({ const validateJsonBody = z.object({
api_key: z.string(),
module: z.string(),
data: z.object({ data: z.object({
content_type: z.object({ content_type: z.object({
uid: z.string(), uid: z.string(),
}), }),
entry: z.object({ entry: z.object({
breadcrumbs: z
.object({
title: z.string(),
})
.optional(),
locale: z.nativeEnum(Lang), locale: z.nativeEnum(Lang),
uid: z.string(), uid: z.string(),
url: z.string().optional(), url: z.string().optional(),
@@ -75,6 +78,25 @@ export async function POST(request: NextRequest) {
console.info(`Revalidating tag: ${tag}`) console.info(`Revalidating tag: ${tag}`)
revalidateTag(tag) revalidateTag(tag)
if (entry.breadcrumbs) {
const breadcrumbsRefsTag = generateRefsResponseTag(
entry.locale,
identifier,
breadcrumbsAffix
)
const breadcrumbsTag = generateTag(
entry.locale,
entry.uid,
breadcrumbsAffix
)
console.info(`Revalidating breadcrumbsRefsTag: ${breadcrumbsRefsTag}`)
revalidateTag(breadcrumbsRefsTag)
console.info(`Revalidating breadcrumbsTag: ${breadcrumbsTag}`)
revalidateTag(breadcrumbsTag)
}
return Response.json({ revalidated: true, now: Date.now() }) return Response.json({ revalidated: true, now: Date.now() })
} catch (error) { } catch (error) {
console.info("Failed to revalidate tag(s)") console.info("Failed to revalidate tag(s)")

View File

@@ -17,7 +17,7 @@ export default async function Footer({ lang }: LangParams) {
locale: lang, locale: lang,
}, },
{ {
tags: [`footer-${lang}`], next: { tags: [`footer-${lang}`] },
} }
) )

View File

@@ -30,7 +30,7 @@ export default async function Header({ lang, uid }: LangParams & HeaderProps) {
const { data } = await request<HeaderQueryData>( const { data } = await request<HeaderQueryData>(
GetHeader, GetHeader,
{ locale: lang }, { locale: lang },
{ tags: [`header-${lang}`] } { next: { tags: [`header-${lang}`] } }
) )
const { data: urls } = await batchRequest<LanguageSwitcherQueryData>([ const { data: urls } = await batchRequest<LanguageSwitcherQueryData>([
{ {

View File

@@ -1,5 +1,9 @@
"use client"
import { cva } from "class-variance-authority" import { cva } from "class-variance-authority"
import { Lang } from "@/constants/languages"
import { trpc } from "@/lib/trpc/client"
import styles from "./maxWidth.module.css" import styles from "./maxWidth.module.css"
import type { MaxWidthProps } from "@/types/components/max-width" import type { MaxWidthProps } from "@/types/components/max-width"
@@ -10,8 +14,11 @@ export default function MaxWidth({
className, className,
tag = "section", tag = "section",
...props ...props
}: MaxWidthProps }: MaxWidthProps) {
) { const d = trpc.contentstack.breadcrumbs.get.useQuery({
locale: Lang.en,
href: "/my-pages/overview",
})
const Cmp = tag const Cmp = tag
return <Cmp className={maxWidthVariants({ className })} {...props} /> return <Cmp className={maxWidthVariants({ className })} {...props} />
} }

View File

@@ -11,7 +11,6 @@ export default function Breadcrumbs({ breadcrumbs }: BreadcrumbsProps) {
return ( return (
<nav className={styles.breadcrumbs}> <nav className={styles.breadcrumbs}>
<ul className={styles.list}> <ul className={styles.list}>
<BreadcrumbsWithLink href="#">{_("Home")}</BreadcrumbsWithLink>
{breadcrumbs.map((breadcrumb) => { {breadcrumbs.map((breadcrumb) => {
if (breadcrumb.href) { if (breadcrumb.href) {
return ( return (

2
env/client.ts vendored
View File

@@ -3,10 +3,12 @@ import { z } from "zod"
export const env = createEnv({ export const env = createEnv({
client: { client: {
NEXT_PUBLIC_NODE_ENV: z.enum(["development", "test", "production"]),
NEXT_PUBLIC_PORT: z.string().default("3000"), NEXT_PUBLIC_PORT: z.string().default("3000"),
}, },
emptyStringAsUndefined: true, emptyStringAsUndefined: true,
runtimeEnv: { runtimeEnv: {
NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV,
NEXT_PUBLIC_PORT: process.env.NEXT_PUBLIC_PORT, NEXT_PUBLIC_PORT: process.env.NEXT_PUBLIC_PORT,
}, },
}) })

View File

@@ -1,7 +1,7 @@
fragment MyPagesBreadcrumbs on AccountPage { fragment MyPagesBreadcrumbs on AccountPage {
breadcrumbs { breadcrumbs {
title title
parents: parentsConnection { parentsConnection {
edges { edges {
node { node {
... on AccountPage { ... on AccountPage {
@@ -9,6 +9,7 @@ fragment MyPagesBreadcrumbs on AccountPage {
title title
} }
system { system {
locale
uid uid
} }
url url

View File

@@ -0,0 +1,20 @@
#import "./System.graphql"
fragment MyPagesBreadcrumbsRefs on AccountPage {
breadcrumbs {
parentsConnection {
edges {
node {
... on AccountPage {
system {
...System
}
}
}
}
}
}
system {
...System
}
}

View File

@@ -1,4 +1,5 @@
#import "../Fragments/MyPages/Breadcrumbs.graphql" #import "../Fragments/MyPages/Breadcrumbs.graphql"
#import "../Fragments/Refs/Breadcrumbs.graphql"
query GetMyPagesBreadcrumbs($locale: String!, $url: String!) { query GetMyPagesBreadcrumbs($locale: String!, $url: String!) {
all_account_page(locale: $locale, where: { url: $url }) { all_account_page(locale: $locale, where: { url: $url }) {
@@ -10,3 +11,11 @@ query GetMyPagesBreadcrumbs($locale: String!, $url: String!) {
} }
} }
} }
query GetMyPagesBreadcrumbsRefs($locale: String!, $url: String!) {
all_account_page(locale: $locale, where: { url: $url }) {
items {
...MyPagesBreadcrumbsRefs
}
}
}

View File

@@ -12,7 +12,9 @@ export async function batchRequest<T>(
try { try {
const response = await Promise.allSettled( const response = await Promise.allSettled(
queries.map((query) => queries.map((query) =>
request<T>(query.document, query.variables, { tags: query.tags }) request<T>(query.document, query.variables, {
next: { tags: query.tags },
})
) )
) )

View File

@@ -10,6 +10,7 @@ import type { DocumentNode } from "graphql"
import type { Data } from "@/types/request" import type { Data } from "@/types/request"
const client = new GraphQLClient(env.CMS_URL, { const client = new GraphQLClient(env.CMS_URL, {
cache: "force-cache",
fetch: cache(async function ( fetch: cache(async function (
url: URL | RequestInfo, url: URL | RequestInfo,
params: RequestInit | undefined params: RequestInit | undefined
@@ -21,11 +22,14 @@ const client = new GraphQLClient(env.CMS_URL, {
export async function request<T>( export async function request<T>(
query: string | DocumentNode, query: string | DocumentNode,
variables?: {}, variables?: {},
next?: NextFetchRequestConfig options?: Pick<RequestInit, "cache" | "next">
): Promise<Data<T>> { ): Promise<Data<T>> {
try { try {
if (next) { if (options?.cache) {
client.requestConfig.next = next client.requestConfig.cache = options.cache
}
if (options?.next) {
client.requestConfig.next = options.next
} }
if (env.PRINT_QUERY) { if (env.PRINT_QUERY) {

View File

@@ -1,7 +1,7 @@
"use client" "use client"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { httpBatchLink } from "@trpc/client" import { httpBatchLink, loggerLink } from "@trpc/client"
import { useState } from "react" import { useState } from "react"
import { env } from "@/env/client" import { env } from "@/env/client"
@@ -14,7 +14,14 @@ function initializeTrpcClient() {
// that trpc and next are running on the same port. // that trpc and next are running on the same port.
return trpc.createClient({ return trpc.createClient({
links: [ links: [
loggerLink({
enabled: (opts) =>
(env.NEXT_PUBLIC_NODE_ENV === "development" &&
typeof window !== "undefined") ||
(opts.direction === "down" && opts.result instanceof Error),
}),
httpBatchLink({ httpBatchLink({
fetch,
transformer, transformer,
/** /**
* This is locally in Next.js * This is locally in Next.js

View File

@@ -7,6 +7,7 @@ import {
overview, overview,
profile, profile,
profileEdit, profileEdit,
stays,
} from "./constants/routes/myPages.js" } from "./constants/routes/myPages.js"
const jiti = createJiti(new URL(import.meta.url).pathname) const jiti = createJiti(new URL(import.meta.url).pathname)
@@ -124,6 +125,12 @@ const nextConfig = {
{ source: profileEdit.fi, destination: "/fi/my-pages/profile/edit" }, { source: profileEdit.fi, destination: "/fi/my-pages/profile/edit" },
{ source: profileEdit.no, destination: "/no/my-pages/profile/edit" }, { source: profileEdit.no, destination: "/no/my-pages/profile/edit" },
{ source: profileEdit.sv, destination: "/sv/my-pages/profile/edit" }, { source: profileEdit.sv, destination: "/sv/my-pages/profile/edit" },
{ source: stays.da, destination: "/da/my-pages/stays" },
{ source: stays.de, destination: "/de/my-pages/stays" },
{ source: stays.fi, destination: "/fi/my-pages/stays" },
{ source: stays.no, destination: "/no/my-pages/stays" },
{ source: stays.sv, destination: "/sv/my-pages/stays" },
], ],
} }
}, },

View File

@@ -1,7 +1,7 @@
import { TRPCError } from "@trpc/server" import { TRPCError } from "@trpc/server"
import { import {
TRPC_ERROR_CODES_BY_NUMBER,
TRPC_ERROR_CODES_BY_KEY, TRPC_ERROR_CODES_BY_KEY,
TRPC_ERROR_CODES_BY_NUMBER,
} from "@trpc/server/rpc" } from "@trpc/server/rpc"
export function unauthorizedError() { export function unauthorizedError() {

View File

@@ -1,10 +1,8 @@
import { z } from "zod"; import { z } from "zod"
import { Lang } from "@/constants/languages"; import { Lang } from "@/constants/languages"
const langs = Object.keys(Lang) as [keyof typeof Lang]
export const getBreadcrumbsInput = z.object({ export const getBreadcrumbsInput = z.object({
href: z.string().min(1, { message: "href is required" }), href: z.string().min(1, { message: "href is required" }),
locale: z.enum(langs), locale: z.nativeEnum(Lang),
}) })

View File

@@ -1,12 +1,40 @@
import { Lang } from "@/constants/languages"
import { z } from "zod" import { z } from "zod"
export const validateBreadcrumbsRefsConstenstackSchema = z.object({
all_account_page: z.object({
items: z.array(
z.object({
breadcrumbs: z.object({
parentsConnection: z.object({
edges: z.array(
z.object({
node: z.object({
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
}),
})
),
}),
}),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
})
),
}),
})
export const validateBreadcrumbsConstenstackSchema = z.object({ export const validateBreadcrumbsConstenstackSchema = z.object({
all_account_page: z.object({ all_account_page: z.object({
items: z.array( items: z.array(
z.object({ z.object({
breadcrumbs: z.object({ breadcrumbs: z.object({
title: z.string(), title: z.string(),
parents: z.object({ parentsConnection: z.object({
edges: z.array( edges: z.array(
z.object({ z.object({
node: z.object({ node: z.object({
@@ -14,6 +42,7 @@ export const validateBreadcrumbsConstenstackSchema = z.object({
title: z.string(), title: z.string(),
}), }),
system: z.object({ system: z.object({
locale: z.nativeEnum(Lang),
uid: z.string(), uid: z.string(),
}), }),
url: z.string(), url: z.string(),

View File

@@ -1,51 +1,120 @@
import { GetMyPagesBreadcrumbs } from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql" import {
GetMyPagesBreadcrumbs,
GetMyPagesBreadcrumbsRefs,
} from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql"
import { request } from "@/lib/graphql/request" import { request } from "@/lib/graphql/request"
import { badRequestError, internalServerError } from "@/server/errors/trpc" import { badRequestError, internalServerError } from "@/server/errors/trpc"
import { publicProcedure, router } from "@/server/trpc" import { publicProcedure, router } from "@/server/trpc"
import { getBreadcrumbsInput } from "./input" import {
import { validateBreadcrumbsConstenstackSchema } from "./output" generateRefsResponseTag,
generateTag,
generateTags,
} from "@/utils/generateTag"
import { removeMultipleSlashes } from "@/utils/url"
import { GetMyPagesBreadcrumbsData } from "@/types/requests/myPages/breadcrumbs" import { getBreadcrumbsInput } from "./input"
import {
getBreadcrumbsSchema,
validateBreadcrumbsConstenstackSchema,
validateBreadcrumbsRefsConstenstackSchema,
} from "./output"
import { affix, getConnections, homeBreadcrumbs } from "./utils"
import type {
GetMyPagesBreadcrumbsData,
GetMyPagesBreadcrumbsRefsData,
} from "@/types/requests/myPages/breadcrumbs"
export const breadcrumbsQueryRouter = router({ export const breadcrumbsQueryRouter = router({
get: publicProcedure.input(getBreadcrumbsInput).query(async ({ input }) => { get: publicProcedure.input(getBreadcrumbsInput).query(async ({ input }) => {
try { try {
const response = await request<GetMyPagesBreadcrumbsData>( const refsResponse = await request<GetMyPagesBreadcrumbsRefsData>(
GetMyPagesBreadcrumbs, GetMyPagesBreadcrumbsRefs,
{ locale: input.locale, url: input.href } { locale: input.locale, url: input.href },
{
next: {
tags: [generateRefsResponseTag(input.locale, input.href, affix)],
},
}
) )
if (!response.data) {
if (!refsResponse.data) {
console.error("Bad response for `GetMyPagesBreadcrumbsRefs`")
console.error({ refsResponse })
throw internalServerError()
}
const validatedRefsData =
validateBreadcrumbsRefsConstenstackSchema.safeParse(refsResponse.data)
if (!validatedRefsData.success) {
console.info("Bad validation for `GetMyPagesBreadcrumbsRefs`")
console.error(validatedRefsData.error)
throw badRequestError() throw badRequestError()
} }
const validatedBreadcrumbs = const connections = getConnections(validatedRefsData.data)
const tags = generateTags(input.locale, connections)
const page = validatedRefsData.data.all_account_page.items[0]
tags.push(generateTag(input.locale, page.system.uid, affix))
const response = await request<GetMyPagesBreadcrumbsData>(
GetMyPagesBreadcrumbs,
{ locale: input.locale, url: input.href },
{ next: { tags } }
)
if (!response.data) {
console.error("Bad response for `GetMyPagesBreadcrumbs`")
console.error({ input })
console.error({ response })
throw internalServerError()
}
const validatedBreadcrumbsData =
validateBreadcrumbsConstenstackSchema.safeParse(response.data) validateBreadcrumbsConstenstackSchema.safeParse(response.data)
if (!validatedBreadcrumbs.success) { if (!validatedBreadcrumbsData.success) {
console.error("Bad validation for `GetMyPagesBreadcrumbs`")
console.error(validatedBreadcrumbsData.error)
throw badRequestError() throw badRequestError()
} }
const parentBreadcrumbs = const parentBreadcrumbs =
validatedBreadcrumbs.data.all_account_page.items[0].breadcrumbs.parents.edges.map( validatedBreadcrumbsData.data.all_account_page.items[0].breadcrumbs.parentsConnection.edges.map(
(breadcrumb) => { (breadcrumb) => {
return { return {
href: breadcrumb.node.url, href: removeMultipleSlashes(
`/${breadcrumb.node.system.locale}/${breadcrumb.node.url}`
),
title: breadcrumb.node.breadcrumbs.title, title: breadcrumb.node.breadcrumbs.title,
uid: breadcrumb.node.system.uid, uid: breadcrumb.node.system.uid,
} }
} }
) )
const pageBreadcrumb =
validatedBreadcrumbs.data.all_account_page.items.map((breadcrumb) => {
return {
href: "",
title: breadcrumb.breadcrumbs.title,
uid: breadcrumb.system.uid,
}
})
const breadcrumbs = [parentBreadcrumbs, pageBreadcrumb].flat()
return breadcrumbs const pageBreadcrumb =
validatedBreadcrumbsData.data.all_account_page.items.map(
(breadcrumb) => {
return {
title: breadcrumb.breadcrumbs.title,
uid: breadcrumb.system.uid,
}
}
)
const breadcrumbs = [
homeBreadcrumbs[input.locale],
parentBreadcrumbs,
pageBreadcrumb,
].flat()
const validatedBreadcrumbs = getBreadcrumbsSchema.safeParse(breadcrumbs)
if (!validatedBreadcrumbs.success) {
console.info("Bad validation for `validatedBreadcrumbs`")
console.error(validatedBreadcrumbs.error)
throw badRequestError()
}
return validatedBreadcrumbs.data
} catch (error) { } catch (error) {
console.info(`Get My Pages Breadcrumbs Error`) console.info(`Get My Pages Breadcrumbs Error`)
console.error(error) console.error(error)

View File

@@ -0,0 +1,50 @@
import { Lang } from "@/constants/languages"
import type { GetMyPagesBreadcrumbsRefsData } from "@/types/requests/myPages/breadcrumbs"
import type { Edges } from "@/types/requests/utils/edges"
import type { NodeRefs } from "@/types/requests/utils/refs"
export function getConnections(refs: GetMyPagesBreadcrumbsRefsData) {
const connections: Edges<NodeRefs>[] = []
refs.all_account_page.items.forEach((ref) => {
connections.push(ref.breadcrumbs.parentsConnection)
})
return connections
}
export const affix = "breadcrumbs"
// TODO: Make these editable in CMS?
export const homeBreadcrumbs = {
[Lang.da]: {
href: "/da",
title: "Hjem",
uid: "da",
},
[Lang.de]: {
href: "/de",
title: "Heim",
uid: "de",
},
[Lang.en]: {
href: "/en",
title: "Home",
uid: "en",
},
[Lang.fi]: {
href: "/fi",
title: "Koti",
uid: "fi",
},
[Lang.no]: {
href: "/no",
title: "Hjem",
uid: "no",
},
[Lang.sv]: {
href: "/sv",
title: "Hem",
uid: "sv",
},
}

View File

@@ -11,6 +11,7 @@ import {
generateTag, generateTag,
generateTags, generateTags,
} from "@/utils/generateTag" } from "@/utils/generateTag"
import { removeMultipleSlashes } from "@/utils/url"
import { getNavigationInputSchema } from "./input" import { getNavigationInputSchema } from "./input"
import { import {
@@ -35,7 +36,7 @@ export function mapMenuItems(navigationItems: NavigationItem[]) {
lang: node.system.locale, lang: node.system.locale,
linkText: item.link_text || node.title, linkText: item.link_text || node.title,
uid: node.system.uid, uid: node.system.uid,
url: `/${node.system.locale}/${node.url}`.replaceAll(/\/\/+/g, "/"), url: removeMultipleSlashes(`/${node.system.locale}/${node.url}`),
} }
if ("sub_items" in item) { if ("sub_items" in item) {
@@ -54,12 +55,16 @@ export const navigationQueryRouter = router({
const refsResponse = await request<GetNavigationMyPagesRefsData>( const refsResponse = await request<GetNavigationMyPagesRefsData>(
GetNavigationMyPagesRefs, GetNavigationMyPagesRefs,
{ locale: lang }, { locale: lang },
{ tags: [generateRefsResponseTag(lang, "navigation_my_pages")] } {
next: {
tags: [generateRefsResponseTag(lang, "navigation_my_pages")],
},
}
) )
if (!refsResponse.data) { if (!refsResponse.data) {
console.error("Bad response for `GetNavigationMyPagesRefs`") console.error("Bad response for `GetNavigationMyPagesRefs`")
console.error(refsResponse) console.error({ refsResponse })
throw internalServerError() throw internalServerError()
} }
@@ -80,12 +85,13 @@ export const navigationQueryRouter = router({
const response = await request<GetNavigationMyPagesData>( const response = await request<GetNavigationMyPagesData>(
GetNavigationMyPages, GetNavigationMyPages,
{ locale: lang }, { locale: lang },
{ tags } { next: { tags } }
) )
if (!response.data) { if (!response.data) {
console.error("Bad response for `GetNavigationMyPages`") console.error("Bad response for `GetNavigationMyPages`")
console.error(response) console.error({ input: lang })
console.error({ response })
throw internalServerError() throw internalServerError()
} }

View File

@@ -31,7 +31,6 @@ export const userQueryRouter = router({
get: protectedProcedure.query(async function ({ ctx }) { get: protectedProcedure.query(async function ({ ctx }) {
try { try {
const apiResponse = await api.get(api.endpoints.v0.profile, { const apiResponse = await api.get(api.endpoints.v0.profile, {
cache: "no-store",
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
}, },

View File

@@ -1,14 +1,17 @@
import { Lang } from "@/constants/languages"
import type { AllRequestResponse } from "../utils/all" import type { AllRequestResponse } from "../utils/all"
import type { EdgesWithTotalCount } from "../utils/edges" import type { Edges } from "../utils/edges"
import type { System } from "../system"
interface AccountPageBreadcrumbs { interface AccountPageBreadcrumbs {
breadcrumbs: { breadcrumbs: {
title: string title: string
parents: EdgesWithTotalCount<{ parentsConnection: Edges<{
breadcrumbs: { breadcrumbs: {
title: string title: string
} }
system: { system: {
locale: Lang
uid: string uid: string
} }
url: string url: string
@@ -25,3 +28,16 @@ interface AllAccountPageResponse
export interface GetMyPagesBreadcrumbsData { export interface GetMyPagesBreadcrumbsData {
all_account_page: AllAccountPageResponse all_account_page: AllAccountPageResponse
} }
interface AccountPageBreadcrumbRefs extends System {
breadcrumbs: {
parentsConnection: Edges<System>
}
}
interface AllAccountPageRefsResponse
extends AllRequestResponse<AccountPageBreadcrumbRefs> { }
export interface GetMyPagesBreadcrumbsRefsData {
all_account_page: AllAccountPageRefsResponse
}

View File

@@ -1,16 +1,25 @@
import type { Edges } from "@/types/requests/utils/edges" import type { Edges } from "@/types/requests/utils/edges"
import type { Lang } from "@/constants/languages"
import type { NodeRefs } from "@/types/requests/utils/refs" import type { NodeRefs } from "@/types/requests/utils/refs"
import type { Lang } from "@/constants/languages"
/** /**
* Function to generate tag for initial refs request * Function to generate tag for initial refs request
* *
* @param lang * @param lang Lang
* @param identifier Should be uri for all pages and content_type_uid for * @param identifier Should be uri for all pages and content_type_uid for
* everything else * everything else
* @param affix possible extra value to add to string, e.g lang:identifier:breadcrumbs:refs
* as it is the same entity as the actual page tag otherwise
* @returns string * @returns string
*/ */
export function generateRefsResponseTag(lang: Lang, identifier: string) { export function generateRefsResponseTag(
lang: Lang,
identifier: string,
affix?: string
) {
if (affix) {
return `${lang}:${identifier}:${affix}:refs`
}
return `${lang}:${identifier}:refs` return `${lang}:${identifier}:refs`
} }
@@ -35,9 +44,15 @@ export function generateRefTag(
* *
* @param lang Lang * @param lang Lang
* @param uid system.uid of entity * @param uid system.uid of entity
* @param affix possible extra value to add to string, e.g lang:uid:breadcrumbs
* as it is the same entity as the actual page tag otherwise
* @returns string * @returns string
*/ */
export function generateTag(lang: Lang, uid: string) { export function generateTag(lang: Lang, uid: string, affix?: string) {
if (affix) {
return `${lang}:${uid}:${affix}`
}
return `${lang}:${uid}` return `${lang}:${uid}`
} }

3
utils/url.ts Normal file
View File

@@ -0,0 +1,3 @@
export function removeMultipleSlashes(str: string) {
return str.replaceAll(/\/\/+/g, "/")
}