From f313275a14a8e9dc04473d5f96d832cbdb09dcc8 Mon Sep 17 00:00:00 2001 From: Matti Nannt Date: Sun, 17 Sep 2023 00:31:38 +0900 Subject: [PATCH] fix: sync call gets called by other functions (#823) * fix: sync call gets called by other functions * fix: sync call gets called by other functions * remove unused imports * remove unused import * fix userId update --- .../people/[personId]/set-attribute/route.ts | 20 +-- .../js/people/[personId]/set-user-id/route.ts | 20 +-- apps/web/app/api/v1/js/sync/lib/sync.ts | 99 +++++++++++ apps/web/app/api/v1/js/sync/route.ts | 162 +----------------- 4 files changed, 108 insertions(+), 193 deletions(-) create mode 100644 apps/web/app/api/v1/js/sync/lib/sync.ts 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 8e6df8802d..0b4abf5967 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,10 +1,10 @@ +import { getUpdatedState } from "@/app/api/v1/js/sync/lib/sync"; import { responses } from "@/lib/api/response"; import { transformErrorToDetails } from "@/lib/api/validator"; import { prisma } from "@formbricks/database"; -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 { ZJsPeopleAttributeInput } from "@formbricks/types/v1/js"; import { revalidateTag } from "next/cache"; import { NextResponse } from "next/server"; @@ -76,21 +76,7 @@ export async function POST(req: Request, { params }): Promise { // revalidate person revalidateTag(personId); - 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"); - } - - const syncJson = await syncRes.json(); - const state: TJsState = syncJson.data; + const state = await getUpdatedState(environmentId, personId, sessionId); return responses.successResponse({ ...state }, true); } catch (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 0021d65abc..2d2197ae70 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,9 +1,9 @@ +import { getUpdatedState } from "@/app/api/v1/js/sync/lib/sync"; import { responses } from "@/lib/api/response"; import { transformErrorToDetails } from "@/lib/api/validator"; import { prisma } from "@formbricks/database"; -import { WEBAPP_URL } from "@formbricks/lib/constants"; import { deletePerson, selectPerson, transformPrismaPerson } from "@formbricks/lib/services/person"; -import { TJsState, ZJsPeopleUserIdInput } from "@formbricks/types/v1/js"; +import { ZJsPeopleUserIdInput } from "@formbricks/types/v1/js"; import { revalidateTag } from "next/cache"; import { NextResponse } from "next/server"; @@ -97,21 +97,7 @@ export async function POST(req: Request, { params }): Promise { 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"); - } - - const syncJson = await syncRes.json(); - const state: TJsState = syncJson.data; + const state = await getUpdatedState(environmentId, person.id, sessionId); return responses.successResponse({ ...state }, true); } catch (error) { diff --git a/apps/web/app/api/v1/js/sync/lib/sync.ts b/apps/web/app/api/v1/js/sync/lib/sync.ts new file mode 100644 index 0000000000..a6a47f54f2 --- /dev/null +++ b/apps/web/app/api/v1/js/sync/lib/sync.ts @@ -0,0 +1,99 @@ +import { getSurveysCached } from "@/app/api/v1/js/surveys"; +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 { TEnvironment } from "@formbricks/types/v1/environment"; +import { TJsState } from "@formbricks/types/v1/js"; +import { TPerson } from "@formbricks/types/v1/people"; +import { TSession } from "@formbricks/types/v1/sessions"; + +const captureNewSessionTelemetry = async (jsVersion?: string): Promise => { + await captureTelemetry("session created", { jsVersion: jsVersion ?? "unknown" }); +}; + +export const getUpdatedState = async ( + environmentId: string, + personId?: string, + sessionId?: string, + jsVersion?: string +): Promise => { + let environment: TEnvironment | null; + let person: TPerson; + let session: TSession | null; + + // check if environment exists + environment = await getEnvironmentCached(environmentId); + + if (!environment) { + throw new Error("Environment does not exist"); + } + + if (!personId) { + // create a new person + person = await createPerson(environmentId); + // create a new session + session = await createSession(person.id); + } else { + // check if person exists + const existingPerson = await getPersonCached(personId); + if (!existingPerson) { + // create a new person + person = await createPerson(environmentId); + } else { + person = existingPerson; + } + } + if (!sessionId) { + // create a new session + session = await createSession(person.id); + } else { + // check validity of person & session + session = await getSessionCached(sessionId); + if (!session) { + // create a new session + session = await createSession(person.id); + captureNewSessionTelemetry(jsVersion); + } else { + // check if session is expired + if (session.expiresAt < new Date()) { + // create a new session + session = await createSession(person.id); + captureNewSessionTelemetry(jsVersion); + } else { + // extend session (if about to expire) + const isSessionAboutToExpire = + new Date(session.expiresAt).getTime() - new Date().getTime() < 1000 * 60 * 10; + + if (isSessionAboutToExpire) { + session = await extendSession(sessionId); + } + } + } + } + // we now have a valid person & session + + // get/create rest of the state + const [surveys, noCodeActionClasses, product] = await Promise.all([ + getSurveysCached(environmentId, person), + getActionClassesCached(environmentId), + getProductByEnvironmentIdCached(environmentId), + ]); + + if (!product) { + throw new Error("Product not found"); + } + + // return state + const state: TJsState = { + person: person!, + session, + surveys, + noCodeActionClasses: noCodeActionClasses.filter((actionClass) => actionClass.type === "noCode"), + product, + }; + + return state; +}; diff --git a/apps/web/app/api/v1/js/sync/route.ts b/apps/web/app/api/v1/js/sync/route.ts index d1c91ea0b1..a8973e4db4 100644 --- a/apps/web/app/api/v1/js/sync/route.ts +++ b/apps/web/app/api/v1/js/sync/route.ts @@ -1,21 +1,9 @@ -import { getSurveysCached } from "@/app/api/v1/js/surveys"; +import { getUpdatedState } from "@/app/api/v1/js/sync/lib/sync"; import { responses } from "@/lib/api/response"; import { transformErrorToDetails } from "@/lib/api/validator"; -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 { ZJsSyncInput } from "@formbricks/types/v1/js"; import { NextResponse } from "next/server"; -const captureNewSessionTelemetry = async (jsVersion?: string): Promise => { - await captureTelemetry("session created", { jsVersion: jsVersion ?? "unknown" }); -}; - export async function OPTIONS(): Promise { return responses.successResponse({}, true); } @@ -37,152 +25,8 @@ export async function POST(req: Request): Promise { const { environmentId, personId, sessionId } = inputValidation.data; - // check if environment exists - const environment = await getEnvironmentCached(environmentId); + const state = await getUpdatedState(environmentId, personId, sessionId, inputValidation.data.jsVersion); - if (!environment) { - return responses.badRequestResponse( - "Environment does not exist", - { environmentId: "Environment with this ID does not exist" }, - true - ); - } - - if (!personId) { - // create a new person - const person = await createPerson(environmentId); - - // create a new session - const session = await createSession(person.id); - - // get/create rest of the state - - const [surveys, noCodeActionClasses, product] = await Promise.all([ - getSurveysCached(environmentId, person), - getActionClassesCached(environmentId), - getProductByEnvironmentIdCached(environmentId), - ]); - - captureNewSessionTelemetry(inputValidation.data.jsVersion); - - 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); - } - - if (!sessionId) { - let person: TPerson | null; - // check if person exists - - person = await getPersonCached(personId); - if (!person) { - // create a new person - person = await createPerson(environmentId); - } - - // create a new session - const session = await createSession(person.id); - - const [surveys, noCodeActionClasses, product] = await Promise.all([ - getSurveysCached(environmentId, person), - getActionClassesCached(environmentId), - getProductByEnvironmentIdCached(environmentId), - ]); - - if (!product) { - return responses.notFoundResponse("ProductByEnvironmentId", environmentId, true); - } - - captureNewSessionTelemetry(inputValidation.data.jsVersion); - - // return state - const state: TJsState = { - person, - session, - surveys, - noCodeActionClasses: noCodeActionClasses.filter((actionClass) => actionClass.type === "noCode"), - product, - }; - - return responses.successResponse({ ...state }, true); - } - // person & session exists - - // check if session exists - let person: TPerson | null; - let session: TSession | null; - - session = await getSessionCached(sessionId); - - if (!session) { - // check if person exits - - person = await getPersonCached(personId); - - if (!person) { - // create a new person - person = await createPerson(environmentId); - } - // create a new session - session = await createSession(person.id); - captureNewSessionTelemetry(inputValidation.data.jsVersion); - } else { - // session exists - // check if person exists (should always exist, but just in case) - - person = await getPersonCached(personId); - if (!person) { - // create a new person & session - person = await createPerson(environmentId); - session = await createSession(person.id); - } else { - // check if session is expired - if (session.expiresAt < new Date()) { - // create a new session - session = await createSession(person.id); - captureNewSessionTelemetry(inputValidation.data.jsVersion); - } else { - // extend session - - const isSessionAboutToExpire = - new Date(session.expiresAt).getTime() - new Date().getTime() < 1000 * 60 * 10; - - if (isSessionAboutToExpire) { - session = await extendSession(sessionId); - } - } - } - } - - // get/create rest of the state - const [surveys, noCodeActionClasses, product] = await Promise.all([ - getSurveysCached(environmentId, person), - getActionClassesCached(environmentId), - getProductByEnvironmentIdCached(environmentId), - ]); - - 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);