diff --git a/apps/web/app/api/v1/js/people/[personId]/set-attribute/route.ts b/apps/web/app/api/v1/js/people/[personId]/set-attribute/route.ts index d767937342..8e6df8802d 100644 --- a/apps/web/app/api/v1/js/people/[personId]/set-attribute/route.ts +++ b/apps/web/app/api/v1/js/people/[personId]/set-attribute/route.ts @@ -1,11 +1,9 @@ -import { getSurveys } from "@/app/api/v1/js/surveys"; import { responses } from "@/lib/api/response"; import { transformErrorToDetails } from "@/lib/api/validator"; import { prisma } from "@formbricks/database"; -import { getActionClasses } from "@formbricks/lib/services/actionClass"; -import { getPerson, selectPerson, transformPrismaPerson } from "@formbricks/lib/services/person"; -import { getProductByEnvironmentId } from "@formbricks/lib/services/product"; -import { extendSession } from "@formbricks/lib/services/session"; +import { WEBAPP_URL } from "@formbricks/lib/constants"; +import { createAttributeClass, getAttributeClassByNameCached } from "@formbricks/lib/services/attributeClass"; +import { getPersonCached } from "@formbricks/lib/services/person"; import { TJsState, ZJsPeopleAttributeInput } from "@formbricks/types/v1/js"; import { revalidateTag } from "next/cache"; import { NextResponse } from "next/server"; @@ -32,45 +30,25 @@ export async function POST(req: Request, { params }): Promise { const { environmentId, sessionId, key, value } = inputValidation.data; - const existingPerson = await getPerson(personId); + const existingPerson = await getPersonCached(personId); if (!existingPerson) { return responses.notFoundResponse("Person", personId, true); } - // find attribute class - let attributeClass = await prisma.attributeClass.findUnique({ - where: { - name_environmentId: { - name: key, - environmentId, - }, - }, - select: { - id: true, - }, - }); + let attributeClass = await getAttributeClassByNameCached(environmentId, key); // create new attribute class if not found if (attributeClass === null) { - attributeClass = await prisma.attributeClass.create({ - data: { - name: key, - type: "code", - environment: { - connect: { - id: environmentId, - }, - }, - }, - select: { - id: true, - }, - }); + attributeClass = await createAttributeClass(environmentId, key, "code"); + } + + if (!attributeClass) { + return responses.internalServerErrorResponse("Unable to create attribute class", true); } // upsert attribute (update or create) - const attribute = await prisma.attribute.upsert({ + await prisma.attribute.upsert({ where: { attributeClassId_personId: { attributeClassId: attributeClass.id, @@ -93,40 +71,27 @@ export async function POST(req: Request, { params }): Promise { }, value, }, - select: { - person: { - select: selectPerson, - }, - }, }); - const person = transformPrismaPerson(attribute.person); + // revalidate person + revalidateTag(personId); - if (person) { - // revalidate person - revalidateTag(person.id); + const syncRes = await fetch(`${WEBAPP_URL}/api/v1/js/sync`, { + method: "POST", + body: JSON.stringify({ + environmentId, + personId, + sessionId, + }), + }); + + if (!syncRes.ok) { + throw new Error("Unable to get latest state from sync"); } - // get/create rest of the state - const [session, surveys, noCodeActionClasses, product] = await Promise.all([ - extendSession(sessionId), - getSurveys(environmentId, person), - getActionClasses(environmentId), - getProductByEnvironmentId(environmentId), - ]); + const syncJson = await syncRes.json(); + const state: TJsState = syncJson.data; - if (!product) { - return responses.notFoundResponse("ProductByEnvironmentId", environmentId, true); - } - - // return state - const state: TJsState = { - person, - session, - surveys, - noCodeActionClasses: noCodeActionClasses.filter((actionClass) => actionClass.type === "noCode"), - product, - }; return responses.successResponse({ ...state }, true); } catch (error) { console.error(error); diff --git a/apps/web/app/api/v1/js/people/[personId]/set-user-id/route.ts b/apps/web/app/api/v1/js/people/[personId]/set-user-id/route.ts index cc8a942ead..0021d65abc 100644 --- a/apps/web/app/api/v1/js/people/[personId]/set-user-id/route.ts +++ b/apps/web/app/api/v1/js/people/[personId]/set-user-id/route.ts @@ -1,11 +1,8 @@ -import { getSurveys } from "@/app/api/v1/js/surveys"; import { responses } from "@/lib/api/response"; import { transformErrorToDetails } from "@/lib/api/validator"; import { prisma } from "@formbricks/database"; -import { getActionClasses } from "@formbricks/lib/services/actionClass"; +import { WEBAPP_URL } from "@formbricks/lib/constants"; import { deletePerson, selectPerson, transformPrismaPerson } from "@formbricks/lib/services/person"; -import { getProductByEnvironmentId } from "@formbricks/lib/services/product"; -import { extendSession } from "@formbricks/lib/services/session"; import { TJsState, ZJsPeopleUserIdInput } from "@formbricks/types/v1/js"; import { revalidateTag } from "next/cache"; import { NextResponse } from "next/server"; @@ -66,6 +63,7 @@ export async function POST(req: Request, { params }): Promise { // delete old person await deletePerson(personId); + returnedPerson = existingPerson; } else { // update person with userId @@ -99,26 +97,22 @@ export async function POST(req: Request, { params }): Promise { revalidateTag(person.id); } - // get/create rest of the state - const [session, surveys, noCodeActionClasses, product] = await Promise.all([ - extendSession(sessionId), - getSurveys(environmentId, person), - getActionClasses(environmentId), - getProductByEnvironmentId(environmentId), - ]); + const syncRes = await fetch(`${WEBAPP_URL}/api/v1/js/sync`, { + method: "POST", + body: JSON.stringify({ + environmentId, + personId, + sessionId, + }), + }); - if (!product) { - return responses.notFoundResponse("ProductByEnvironmentId", environmentId, true); + if (!syncRes.ok) { + throw new Error("Unable to get latest state from sync"); } - // return state - const state: TJsState = { - person, - session, - surveys, - noCodeActionClasses: noCodeActionClasses.filter((actionClass) => actionClass.type === "noCode"), - product, - }; + const syncJson = await syncRes.json(); + const state: TJsState = syncJson.data; + return responses.successResponse({ ...state }, true); } catch (error) { console.error(error); diff --git a/apps/web/app/api/v1/js/surveys.ts b/apps/web/app/api/v1/js/surveys.ts index 831d6404b1..8b53d212ce 100644 --- a/apps/web/app/api/v1/js/surveys.ts +++ b/apps/web/app/api/v1/js/surveys.ts @@ -2,6 +2,29 @@ import { prisma } from "@formbricks/database"; import { selectSurvey } from "@formbricks/lib/services/survey"; import { TPerson } from "@formbricks/types/v1/people"; import { TSurvey } from "@formbricks/types/v1/surveys"; +import { unstable_cache } from "next/cache"; + +const getSurveysCacheTags = (environmentId: string, personId: string): string[] => [ + `env-${environmentId}-surveys`, + `env-${environmentId}-product`, + personId, +]; + +const getSurveysCacheKey = (environmentId: string, personId: string): string[] => [ + `env-${environmentId}-person-${personId}-syncSurveys`, +]; + +export const getSurveysCached = (environmentId: string, person: TPerson) => + unstable_cache( + async () => { + return await getSurveys(environmentId, person); + }, + getSurveysCacheKey(environmentId, person.id), + { + tags: getSurveysCacheTags(environmentId, person.id), + revalidate: 30 * 60, + } + )(); export const getSurveys = async (environmentId: string, person: TPerson): Promise => { // get recontactDays from product diff --git a/apps/web/app/api/v1/js/sync/route.ts b/apps/web/app/api/v1/js/sync/route.ts index 51ac378219..d1c91ea0b1 100644 --- a/apps/web/app/api/v1/js/sync/route.ts +++ b/apps/web/app/api/v1/js/sync/route.ts @@ -1,37 +1,21 @@ -import { getSurveys } from "@/app/api/v1/js/surveys"; +import { getSurveysCached } from "@/app/api/v1/js/surveys"; import { responses } from "@/lib/api/response"; import { transformErrorToDetails } from "@/lib/api/validator"; -import { getActionClasses } from "@formbricks/lib/services/actionClass"; -import { getEnvironment } from "@formbricks/lib/services/environment"; -import { createPerson, getPerson } from "@formbricks/lib/services/person"; -import { getProductByEnvironmentId } from "@formbricks/lib/services/product"; -import { createSession, extendSession, getSession } from "@formbricks/lib/services/session"; +import { getActionClassesCached } from "@formbricks/lib/services/actionClass"; +import { getEnvironmentCached } from "@formbricks/lib/services/environment"; +import { createPerson, getPersonCached } from "@formbricks/lib/services/person"; +import { getProductByEnvironmentIdCached } from "@formbricks/lib/services/product"; +import { createSession, extendSession, getSessionCached } from "@formbricks/lib/services/session"; import { captureTelemetry } from "@formbricks/lib/telemetry"; import { TJsState, ZJsSyncInput } from "@formbricks/types/v1/js"; import { TPerson } from "@formbricks/types/v1/people"; import { TSession } from "@formbricks/types/v1/sessions"; -import { unstable_cache } from "next/cache"; import { NextResponse } from "next/server"; const captureNewSessionTelemetry = async (jsVersion?: string): Promise => { await captureTelemetry("session created", { jsVersion: jsVersion ?? "unknown" }); }; -const getEnvironmentCacheKey = (environmentId: string): string[] => ["environment", environmentId]; -const getPersonCacheKey = (personId: string): string[] => ["person", personId]; -const getSessionCacheKey = (sessionId: string): string[] => ["session", sessionId]; -const getSurveysCacheKey = (environmentId: string, personId: string): string[] => [ - "surveys", - `env-${environmentId}-surveys`, - `env-${environmentId}-product`, - "person", - personId, -]; -const getProductCacheKey = (environmentId: string): string[] => [`env-${environmentId}-product`]; -const getActionClassesCacheKey = (environmentId: string): string[] => [`env-${environmentId}-actionClasses`]; - -const halfHourSeconds = 30 * 60; - export async function OPTIONS(): Promise { return responses.successResponse({}, true); } @@ -54,17 +38,6 @@ export async function POST(req: Request): Promise { const { environmentId, personId, sessionId } = inputValidation.data; // check if environment exists - const getEnvironmentCached = unstable_cache( - async (environmentId: string) => { - return await getEnvironment(environmentId); - }, - getEnvironmentCacheKey(environmentId), - { - tags: getEnvironmentCacheKey(environmentId), - revalidate: halfHourSeconds, - } - ); - const environment = await getEnvironmentCached(environmentId); if (!environment) { @@ -82,39 +55,6 @@ export async function POST(req: Request): Promise { // create a new session const session = await createSession(person.id); - const getSurveysCached = unstable_cache( - async (environmentId: string, person: TPerson) => { - return await getSurveys(environmentId, person); - }, - getSurveysCacheKey(environmentId, person.id), - { - tags: getSurveysCacheKey(environmentId, person.id), - revalidate: halfHourSeconds, - } - ); - - const getActionClassesCached = unstable_cache( - async (environmentId: string) => { - return await getActionClasses(environmentId); - }, - getActionClassesCacheKey(environmentId), - { - tags: getActionClassesCacheKey(environmentId), - revalidate: halfHourSeconds, - } - ); - - const getProductByEnvironmentIdCached = unstable_cache( - async (environmentId: string) => { - return await getProductByEnvironmentId(environmentId); - }, - getProductCacheKey(environmentId), - { - tags: getProductCacheKey(environmentId), - revalidate: halfHourSeconds, - } - ); - // get/create rest of the state const [surveys, noCodeActionClasses, product] = await Promise.all([ @@ -144,17 +84,6 @@ export async function POST(req: Request): Promise { let person: TPerson | null; // check if person exists - const getPersonCached = unstable_cache( - async (personId: string) => { - return await getPerson(personId); - }, - getPersonCacheKey(personId), - { - tags: getPersonCacheKey(personId), - revalidate: halfHourSeconds, - } - ); - person = await getPersonCached(personId); if (!person) { // create a new person @@ -164,39 +93,6 @@ export async function POST(req: Request): Promise { // create a new session const session = await createSession(person.id); - const getSurveysCached = unstable_cache( - async (environmentId: string, person: TPerson) => { - return await getSurveys(environmentId, person); - }, - getSurveysCacheKey(environmentId, person.id), - { - tags: getSurveysCacheKey(environmentId, person.id), - revalidate: halfHourSeconds, - } - ); - - const getActionClassesCached = unstable_cache( - async (environmentId: string) => { - return await getActionClasses(environmentId); - }, - getActionClassesCacheKey(environmentId), - { - tags: getActionClassesCacheKey(environmentId), - revalidate: halfHourSeconds, - } - ); - - const getProductByEnvironmentIdCached = unstable_cache( - async (environmentId: string) => { - return await getProductByEnvironmentId(environmentId); - }, - getProductCacheKey(environmentId), - { - tags: getProductCacheKey(environmentId), - revalidate: halfHourSeconds, - } - ); - const [surveys, noCodeActionClasses, product] = await Promise.all([ getSurveysCached(environmentId, person), getActionClassesCached(environmentId), @@ -226,33 +122,11 @@ export async function POST(req: Request): Promise { let person: TPerson | null; let session: TSession | null; - const getSessionCached = unstable_cache( - async (sessionId: string) => { - return await getSession(sessionId); - }, - getSessionCacheKey(sessionId), - { - tags: getSessionCacheKey(sessionId), - revalidate: halfHourSeconds, - } - ); - session = await getSessionCached(sessionId); if (!session) { // check if person exits - const getPersonCached = unstable_cache( - async (personId: string) => { - return await getPerson(personId); - }, - getPersonCacheKey(personId), - { - tags: getPersonCacheKey(personId), - revalidate: halfHourSeconds, - } - ); - person = await getPersonCached(personId); if (!person) { @@ -265,16 +139,6 @@ export async function POST(req: Request): Promise { } else { // session exists // check if person exists (should always exist, but just in case) - const getPersonCached = unstable_cache( - async (personId: string) => { - return await getPerson(personId); - }, - getPersonCacheKey(personId), - { - tags: getPersonCacheKey(personId), - revalidate: halfHourSeconds, - } - ); person = await getPersonCached(personId); if (!person) { @@ -300,39 +164,6 @@ export async function POST(req: Request): Promise { } } - const getSurveysCached = unstable_cache( - async (environmentId: string, person: TPerson) => { - return await getSurveys(environmentId, person); - }, - getSurveysCacheKey(environmentId, person.id), - { - tags: getSurveysCacheKey(environmentId, person.id), - revalidate: halfHourSeconds, - } - ); - - const getActionClassesCached = unstable_cache( - async (environmentId: string) => { - return await getActionClasses(environmentId); - }, - getActionClassesCacheKey(environmentId), - { - tags: getActionClassesCacheKey(environmentId), - revalidate: halfHourSeconds, - } - ); - - const getProductByEnvironmentIdCached = unstable_cache( - async (environmentId: string) => { - return await getProductByEnvironmentId(environmentId); - }, - getProductCacheKey(environmentId), - { - tags: getProductCacheKey(environmentId), - revalidate: halfHourSeconds, - } - ); - // get/create rest of the state const [surveys, noCodeActionClasses, product] = await Promise.all([ getSurveysCached(environmentId, person), diff --git a/packages/lib/services/actionClass.ts b/packages/lib/services/actionClass.ts index 6565c948da..a74185edb9 100644 --- a/packages/lib/services/actionClass.ts +++ b/packages/lib/services/actionClass.ts @@ -3,11 +3,16 @@ import "server-only"; import { prisma } from "@formbricks/database"; import { TActionClass, TActionClassInput, ZActionClassInput } from "@formbricks/types/v1/actionClasses"; -import { validateInputs } from "../utils/validate"; import { ZId } from "@formbricks/types/v1/environment"; -import { cache } from "react"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors"; -import { revalidateTag } from "next/cache"; +import { revalidateTag, unstable_cache } from "next/cache"; +import { cache } from "react"; +import { validateInputs } from "../utils/validate"; + +const getActionClassesCacheTag = (environmentId: string): string => `env-${environmentId}-actionClasses`; +const getActionClassesCacheKey = (environmentId: string): string[] => [ + getActionClassesCacheTag(environmentId), +]; const select = { id: true, @@ -39,6 +44,18 @@ export const getActionClasses = cache(async (environmentId: string): Promise + unstable_cache( + async () => { + return await getActionClasses(environmentId); + }, + getActionClassesCacheKey(environmentId), + { + tags: getActionClassesCacheKey(environmentId), + revalidate: 30 * 60, // 30 minutes + } + )(); + export const deleteActionClass = async ( environmentId: string, actionClassId: string @@ -54,7 +71,7 @@ export const deleteActionClass = async ( if (result === null) throw new ResourceNotFoundError("Action", actionClassId); // revalidate cache - revalidateTag(`env-${environmentId}-actionClasses`); + revalidateTag(getActionClassesCacheTag(environmentId)); return result; } catch (error) { @@ -84,7 +101,7 @@ export const createActionClass = async ( }); // revalidate cache - revalidateTag(`env-${environmentId}-actionClasses`); + revalidateTag(getActionClassesCacheTag(environmentId)); return result; } catch (error) { @@ -115,7 +132,7 @@ export const updateActionClass = async ( }); // revalidate cache - revalidateTag(`env-${environmentId}-actionClasses`); + revalidateTag(getActionClassesCacheTag(environmentId)); return result; } catch (error) { diff --git a/packages/lib/services/attributeClass.ts b/packages/lib/services/attributeClass.ts index 573b70c804..988bcb3081 100644 --- a/packages/lib/services/attributeClass.ts +++ b/packages/lib/services/attributeClass.ts @@ -3,6 +3,7 @@ import "server-only"; import { prisma } from "@formbricks/database"; import { TAttributeClass, + TAttributeClassType, TAttributeClassUpdateInput, ZAttributeClassUpdateInput, } from "@formbricks/types/v1/attributeClasses"; @@ -10,6 +11,13 @@ import { ZId } from "@formbricks/types/v1/environment"; import { validateInputs } from "../utils/validate"; import { DatabaseError } from "@formbricks/types/v1/errors"; import { cache } from "react"; +import { revalidateTag, unstable_cache } from "next/cache"; + +const attributeClassesCacheTag = (environmentId: string): string => `env-${environmentId}-attributeClasses`; + +const getAttributeClassesCacheKey = (environmentId: string): string[] => [ + attributeClassesCacheTag(environmentId), +]; export const transformPrismaAttributeClass = (attributeClass: any): TAttributeClass | null => { if (attributeClass === null) { @@ -43,6 +51,7 @@ export const getAttributeClasses = cache(async (environmentId: string): Promise< throw new DatabaseError(`Database error when fetching attributeClasses for environment ${environmentId}`); } }); + export const updatetAttributeClass = async ( attributeClassId: string, data: Partial @@ -60,12 +69,25 @@ export const updatetAttributeClass = async ( }); const transformedAttributeClass: TAttributeClass | null = transformPrismaAttributeClass(attributeClass); + revalidateTag(attributeClassesCacheTag(attributeClass.environmentId)); return transformedAttributeClass; } catch (error) { throw new DatabaseError(`Database error when updating attribute class with id ${attributeClassId}`); } }; +export const getAttributeClassByNameCached = async (environmentId: string, name: string) => + await unstable_cache( + async () => { + return await getAttributeClassByName(environmentId, name); + }, + getAttributeClassesCacheKey(environmentId), + { + tags: getAttributeClassesCacheKey(environmentId), + revalidate: 30 * 60, // 30 minutes + } + )(); + export const getAttributeClassByName = cache( async (environmentId: string, name: string): Promise => { const attributeClass = await prisma.attributeClass.findFirst({ @@ -77,3 +99,22 @@ export const getAttributeClassByName = cache( return transformPrismaAttributeClass(attributeClass); } ); + +export const createAttributeClass = async ( + environmentId: string, + name: string, + type: TAttributeClassType +): Promise => { + const attributeClass = await prisma.attributeClass.create({ + data: { + name, + type, + environment: { + connect: { + id: environmentId, + }, + }, + }, + }); + return transformPrismaAttributeClass(attributeClass); +}; diff --git a/packages/lib/services/displays.ts b/packages/lib/services/displays.ts index cec20cd679..3072d7bb3e 100644 --- a/packages/lib/services/displays.ts +++ b/packages/lib/services/displays.ts @@ -5,12 +5,13 @@ import { TDisplaysWithSurveyName, ZDisplayInput, } from "@formbricks/types/v1/displays"; -import { Prisma } from "@prisma/client"; -import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors"; -import { transformPrismaPerson } from "./person"; -import { validateInputs } from "../utils/validate"; import { ZId } from "@formbricks/types/v1/environment"; +import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors"; +import { Prisma } from "@prisma/client"; +import { revalidateTag } from "next/cache"; import { cache } from "react"; +import { validateInputs } from "../utils/validate"; +import { transformPrismaPerson } from "./person"; const selectDisplay = { id: true, @@ -65,6 +66,10 @@ export const createDisplay = async (displayInput: TDisplayInput): Promise => { validateInputs([environmentId, ZId]); @@ -38,6 +39,18 @@ export const getEnvironment = cache(async (environmentId: string): Promise + unstable_cache( + async () => { + return await getEnvironment(environmentId); + }, + [environmentId], + { + tags: [environmentId], + revalidate: 30 * 60, // 30 minutes + } + )(); + export const getEnvironments = cache(async (productId: string): Promise => { validateInputs([productId, ZId]); let productPrisma; diff --git a/packages/lib/services/person.ts b/packages/lib/services/person.ts index 4480aac501..652e28376d 100644 --- a/packages/lib/services/person.ts +++ b/packages/lib/services/person.ts @@ -1,14 +1,14 @@ import "server-only"; import { prisma } from "@formbricks/database"; +import { ZId } from "@formbricks/types/v1/environment"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors"; import { TPerson } from "@formbricks/types/v1/people"; import { Prisma } from "@prisma/client"; +import { revalidateTag, unstable_cache } from "next/cache"; import { cache } from "react"; import { validateInputs } from "../utils/validate"; -import { ZId } from "@formbricks/types/v1/environment"; import { getAttributeClassByName } from "./attributeClass"; -import { revalidateTag } from "next/cache"; export const selectPerson = { id: true, @@ -86,6 +86,20 @@ export const getPerson = cache(async (personId: string): Promise } }); +const getPersonCacheKey = (personId: string): string[] => [personId]; + +export const getPersonCached = async (personId: string) => + await unstable_cache( + async () => { + return await getPerson(personId); + }, + getPersonCacheKey(personId), + { + tags: getPersonCacheKey(personId), + revalidate: 30 * 60, // 30 minutes + } + )(); + export const getPeople = cache(async (environmentId: string): Promise => { validateInputs([environmentId, ZId]); try { diff --git a/packages/lib/services/product.ts b/packages/lib/services/product.ts index 18237f5881..daeb672076 100644 --- a/packages/lib/services/product.ts +++ b/packages/lib/services/product.ts @@ -1,14 +1,17 @@ -import "server-only"; import { prisma } from "@formbricks/database"; -import { z } from "zod"; -import { Prisma } from "@prisma/client"; -import { ZProduct, ZProductUpdateInput } from "@formbricks/types/v1/product"; +import { ZId } from "@formbricks/types/v1/environment"; import { DatabaseError, ValidationError } from "@formbricks/types/v1/errors"; import type { TProduct, TProductUpdateInput } from "@formbricks/types/v1/product"; +import { ZProduct, ZProductUpdateInput } from "@formbricks/types/v1/product"; +import { Prisma } from "@prisma/client"; +import { revalidateTag, unstable_cache } from "next/cache"; import { cache } from "react"; +import "server-only"; +import { z } from "zod"; import { validateInputs } from "../utils/validate"; -import { ZId } from "@formbricks/types/v1/environment"; -import { revalidateTag } from "next/cache"; + +const getProductCacheTag = (environmentId: string): string => `env-${environmentId}-product`; +const getProductCacheKey = (environmentId: string): string[] => [getProductCacheTag(environmentId)]; const selectProduct = { id: true, @@ -73,6 +76,18 @@ export const getProductByEnvironmentId = cache(async (environmentId: string): Pr } }); +export const getProductByEnvironmentIdCached = (environmentId: string) => + unstable_cache( + async () => { + return await getProductByEnvironmentId(environmentId); + }, + getProductCacheKey(environmentId), + { + tags: getProductCacheKey(environmentId), + revalidate: 30 * 60, // 30 minutes + } + )(); + export const updateProduct = async ( productId: string, inputProduct: Partial @@ -100,7 +115,7 @@ export const updateProduct = async ( product.environments.forEach((environment) => { // revalidate environment cache - revalidateTag(`env-${environment.id}-product`); + revalidateTag(getProductCacheTag(environment.id)); }); return product; @@ -142,7 +157,8 @@ export const deleteProduct = cache(async (productId: string): Promise if (product) { product.environments.forEach((environment) => { // revalidate product cache - revalidateTag(`env-${environment.id}-product`); + revalidateTag(getProductCacheTag(environment.id)); + revalidateTag(environment.id); }); } diff --git a/packages/lib/services/session.ts b/packages/lib/services/session.ts index 8b1dff515f..6a00af8e10 100644 --- a/packages/lib/services/session.ts +++ b/packages/lib/services/session.ts @@ -2,13 +2,13 @@ import "server-only"; import { prisma } from "@formbricks/database"; +import { ZId } from "@formbricks/types/v1/environment"; import { DatabaseError } from "@formbricks/types/v1/errors"; import { TSession, TSessionWithActions } from "@formbricks/types/v1/sessions"; import { Prisma } from "@prisma/client"; +import { revalidateTag, unstable_cache } from "next/cache"; import { cache } from "react"; import { validateInputs } from "../utils/validate"; -import { ZId } from "@formbricks/types/v1/environment"; -import { revalidateTag } from "next/cache"; const select = { id: true, @@ -40,6 +40,18 @@ export const getSession = async (sessionId: string): Promise => } }; +export const getSessionCached = (sessionId: string) => + unstable_cache( + async () => { + return await getSession(sessionId); + }, + [sessionId], + { + tags: [sessionId], + revalidate: 30 * 60, // 30 minutes + } + )(); + export const getSessionWithActionsOfPerson = async ( personId: string ): Promise => { diff --git a/packages/types/v1/attributeClasses.ts b/packages/types/v1/attributeClasses.ts index b2c8ebfa60..e3075aaf2f 100644 --- a/packages/types/v1/attributeClasses.ts +++ b/packages/types/v1/attributeClasses.ts @@ -1,12 +1,16 @@ import z from "zod"; +export const ZAttributeClassType = z.enum(["code", "noCode", "automatic"]); + +export type TAttributeClassType = z.infer; + export const ZAttributeClass = z.object({ id: z.string().cuid2(), createdAt: z.date(), updatedAt: z.date(), name: z.string(), description: z.string(), - type: z.enum(["code", "noCode", "automatic"]), + type: ZAttributeClassType, environmentId: z.string(), archived: z.boolean(), });