From bf39b0fbfb57a52ceb0d8025e5397572227ce4fa Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Wed, 2 Apr 2025 10:49:27 +0530 Subject: [PATCH] fix: added cache no-store when formbricksDebug is enabled (#5197) Co-authored-by: pandeymangg --- packages/api/src/api/client/attribute.ts | 31 ----- packages/api/src/api/client/display.ts | 20 --- packages/api/src/api/client/environment.ts | 12 +- packages/api/src/api/client/index.ts | 19 +-- packages/api/src/api/client/response.ts | 41 ------ packages/api/src/api/client/storage.ts | 127 ------------------ packages/api/src/api/client/user.ts | 18 ++- packages/api/src/types/index.ts | 1 + packages/api/src/utils/make-request.ts | 5 +- packages/js-core/src/lib/common/setup.ts | 15 ++- packages/js-core/src/lib/environment/state.ts | 4 +- .../src/lib/environment/tests/state.test.ts | 1 + .../js-core/src/lib/user/tests/update.test.ts | 1 + packages/js-core/src/lib/user/update.ts | 4 +- 14 files changed, 48 insertions(+), 251 deletions(-) delete mode 100644 packages/api/src/api/client/attribute.ts delete mode 100644 packages/api/src/api/client/display.ts delete mode 100644 packages/api/src/api/client/response.ts delete mode 100644 packages/api/src/api/client/storage.ts diff --git a/packages/api/src/api/client/attribute.ts b/packages/api/src/api/client/attribute.ts deleted file mode 100644 index 42a64451da..0000000000 --- a/packages/api/src/api/client/attribute.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { type TAttributeUpdateInput } from "@formbricks/types/attributes"; -import { type Result } from "@formbricks/types/error-handlers"; -import { type ApiErrorResponse } from "@formbricks/types/errors"; -import { makeRequest } from "../../utils/make-request"; - -export class AttributeAPI { - private appUrl: string; - private environmentId: string; - - constructor(appUrl: string, environmentId: string) { - this.appUrl = appUrl; - this.environmentId = environmentId; - } - - async update( - attributeUpdateInput: Omit - ): Promise> { - // transform all attributes to string if attributes are present into a new attributes copy - const attributes: Record = {}; - for (const key in attributeUpdateInput.attributes) { - attributes[key] = String(attributeUpdateInput.attributes[key]); - } - - return makeRequest( - this.appUrl, - `/api/v1/client/${this.environmentId}/contacts/${attributeUpdateInput.userId}/attributes`, - "PUT", - { attributes } - ); - } -} diff --git a/packages/api/src/api/client/display.ts b/packages/api/src/api/client/display.ts deleted file mode 100644 index 6fccc24835..0000000000 --- a/packages/api/src/api/client/display.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { type TDisplayCreateInput } from "@formbricks/types/displays"; -import { type Result } from "@formbricks/types/error-handlers"; -import { type ApiErrorResponse } from "@formbricks/types/errors"; -import { makeRequest } from "../../utils/make-request"; - -export class DisplayAPI { - private appUrl: string; - private environmentId: string; - - constructor(appUrl: string, environmentId: string) { - this.appUrl = appUrl; - this.environmentId = environmentId; - } - - async create( - displayInput: Omit - ): Promise> { - return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/displays`, "POST", displayInput); - } -} diff --git a/packages/api/src/api/client/environment.ts b/packages/api/src/api/client/environment.ts index de19b6f701..55488c8e85 100644 --- a/packages/api/src/api/client/environment.ts +++ b/packages/api/src/api/client/environment.ts @@ -6,13 +6,21 @@ import { makeRequest } from "../../utils/make-request"; export class EnvironmentAPI { private appUrl: string; private environmentId: string; + private isDebug: boolean; - constructor(appUrl: string, environmentId: string) { + constructor(appUrl: string, environmentId: string, isDebug: boolean) { this.appUrl = appUrl; this.environmentId = environmentId; + this.isDebug = isDebug; } async getState(): Promise> { - return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/environment`, "GET"); + return makeRequest( + this.appUrl, + `/api/v1/client/${this.environmentId}/environment`, + "GET", + undefined, + this.isDebug + ); } } diff --git a/packages/api/src/api/client/index.ts b/packages/api/src/api/client/index.ts index 321201dac0..01fbeb01ba 100644 --- a/packages/api/src/api/client/index.ts +++ b/packages/api/src/api/client/index.ts @@ -1,27 +1,16 @@ import { type ApiConfig } from "../../types"; -import { AttributeAPI } from "./attribute"; -import { DisplayAPI } from "./display"; import { EnvironmentAPI } from "./environment"; -import { ResponseAPI } from "./response"; -import { StorageAPI } from "./storage"; import { UserAPI } from "./user"; export class Client { - response: ResponseAPI; - display: DisplayAPI; - storage: StorageAPI; - attribute: AttributeAPI; user: UserAPI; environment: EnvironmentAPI; constructor(options: ApiConfig) { - const { appUrl, environmentId } = options; + const { appUrl, environmentId, isDebug } = options; + const isDebugMode = isDebug ?? false; - this.response = new ResponseAPI(appUrl, environmentId); - this.display = new DisplayAPI(appUrl, environmentId); - this.attribute = new AttributeAPI(appUrl, environmentId); - this.storage = new StorageAPI(appUrl, environmentId); - this.user = new UserAPI(appUrl, environmentId); - this.environment = new EnvironmentAPI(appUrl, environmentId); + this.user = new UserAPI(appUrl, environmentId, isDebugMode); + this.environment = new EnvironmentAPI(appUrl, environmentId, isDebugMode); } } diff --git a/packages/api/src/api/client/response.ts b/packages/api/src/api/client/response.ts deleted file mode 100644 index c3044f90ca..0000000000 --- a/packages/api/src/api/client/response.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { type Result } from "@formbricks/types/error-handlers"; -import { type ApiErrorResponse } from "@formbricks/types/errors"; -import { type TResponseInput, type TResponseUpdateInput } from "@formbricks/types/responses"; -import { makeRequest } from "../../utils/make-request"; - -type TResponseUpdateInputWithResponseId = TResponseUpdateInput & { responseId: string }; - -export class ResponseAPI { - private appUrl: string; - private environmentId: string; - - constructor(appUrl: string, environmentId: string) { - this.appUrl = appUrl; - this.environmentId = environmentId; - } - - async create( - responseInput: Omit - ): Promise> { - return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/responses`, "POST", responseInput); - } - - async update({ - responseId, - finished, - endingId, - data, - ttc, - variables, - language, - }: TResponseUpdateInputWithResponseId): Promise> { - return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/responses/${responseId}`, "PUT", { - finished, - endingId, - data, - ttc, - variables, - language, - }); - } -} diff --git a/packages/api/src/api/client/storage.ts b/packages/api/src/api/client/storage.ts deleted file mode 100644 index 45f16919a8..0000000000 --- a/packages/api/src/api/client/storage.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* eslint-disable no-console -- used for error logging */ -import type { TUploadFileConfig, TUploadFileResponse } from "@formbricks/types/storage"; - -export class StorageAPI { - private appUrl: string; - private environmentId: string; - - constructor(appUrl: string, environmentId: string) { - this.appUrl = appUrl; - this.environmentId = environmentId; - } - - async uploadFile( - file: { - type: string; - name: string; - base64: string; - }, - { allowedFileExtensions, surveyId }: TUploadFileConfig | undefined = {} - ): Promise { - if (!file.name || !file.type || !file.base64) { - throw new Error(`Invalid file object`); - } - - const payload = { - fileName: file.name, - fileType: file.type, - allowedFileExtensions, - surveyId, - }; - - const response = await fetch(`${this.appUrl}/api/v1/client/${this.environmentId}/storage`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - throw new Error(`Upload failed with status: ${String(response.status)}`); - } - - const json = (await response.json()) as TUploadFileResponse; - - const { data } = json; - - const { signedUrl, fileUrl, signingData, presignedFields, updatedFileName } = data; - - let localUploadDetails: Record = {}; - - if (signingData) { - const { signature, timestamp, uuid } = signingData; - - localUploadDetails = { - fileType: file.type, - fileName: encodeURIComponent(updatedFileName), - surveyId: surveyId ?? "", - signature, - timestamp: String(timestamp), - uuid, - }; - } - - const formData: Record = {}; - const formDataForS3 = new FormData(); - - if (presignedFields) { - Object.entries(presignedFields).forEach(([key, value]) => { - formDataForS3.append(key, value); - }); - - try { - const binaryString = atob(file.base64.split(",")[1]); - const uint8Array = Uint8Array.from([...binaryString].map((char) => char.charCodeAt(0))); - const blob = new Blob([uint8Array], { type: file.type }); - - formDataForS3.append("file", blob); - } catch (err) { - console.error(err); - throw new Error("Error uploading file"); - } - } - - formData.fileBase64String = file.base64; - - let uploadResponse: Response = {} as Response; - - const signedUrlCopy = signedUrl.replace("http://localhost:3000", this.appUrl); - - try { - uploadResponse = await fetch(signedUrlCopy, { - method: "POST", - body: presignedFields - ? formDataForS3 - : JSON.stringify({ - ...formData, - ...localUploadDetails, - }), - }); - } catch (err) { - console.error("Error uploading file", err); - } - - if (!uploadResponse.ok) { - // if local storage is used, we'll use the json response: - if (signingData) { - const uploadJson = (await uploadResponse.json()) as { message: string }; - const error = new Error(uploadJson.message); - error.name = "FileTooLargeError"; - throw error; - } - - // if s3 is used, we'll use the text response: - const errorText = await uploadResponse.text(); - if (presignedFields && errorText.includes("EntityTooLarge")) { - const error = new Error("File size exceeds the size limit for your plan"); - error.name = "FileTooLargeError"; - throw error; - } - - throw new Error(`Upload failed with status: ${String(uploadResponse.status)}`); - } - - return fileUrl; - } -} diff --git a/packages/api/src/api/client/user.ts b/packages/api/src/api/client/user.ts index 2b0608d3b5..f1ecf3793a 100644 --- a/packages/api/src/api/client/user.ts +++ b/packages/api/src/api/client/user.ts @@ -5,10 +5,12 @@ import { makeRequest } from "../../utils/make-request"; export class UserAPI { private appUrl: string; private environmentId: string; + private isDebug: boolean; - constructor(appUrl: string, environmentId: string) { + constructor(appUrl: string, environmentId: string, isDebug: boolean) { this.appUrl = appUrl; this.environmentId = environmentId; + this.isDebug = isDebug; } async createOrUpdate(userUpdateInput: { userId: string; attributes?: Record }): Promise< @@ -37,9 +39,15 @@ export class UserAPI { attributes[key] = String(userUpdateInput.attributes[key]); } - return makeRequest(this.appUrl, `/api/v2/client/${this.environmentId}/user`, "POST", { - userId: userUpdateInput.userId, - attributes, - }); + return makeRequest( + this.appUrl, + `/api/v2/client/${this.environmentId}/user`, + "POST", + { + userId: userUpdateInput.userId, + attributes, + }, + this.isDebug + ); } } diff --git a/packages/api/src/types/index.ts b/packages/api/src/types/index.ts index f4b01d271c..054535db99 100644 --- a/packages/api/src/types/index.ts +++ b/packages/api/src/types/index.ts @@ -3,6 +3,7 @@ import { type ApiErrorResponse } from "@formbricks/types/errors"; export interface ApiConfig { environmentId: string; appUrl: string; + isDebug?: boolean; } export type ApiResponse = ApiSuccessResponse | ApiErrorResponse; diff --git a/packages/api/src/utils/make-request.ts b/packages/api/src/utils/make-request.ts index ba24bb796a..cb1a7c8e6a 100644 --- a/packages/api/src/utils/make-request.ts +++ b/packages/api/src/utils/make-request.ts @@ -6,15 +6,16 @@ export const makeRequest = async ( appUrl: string, endpoint: string, method: "GET" | "POST" | "PUT" | "DELETE", - data?: unknown + data?: unknown, + isDebug?: boolean ): Promise> => { const url = new URL(appUrl + endpoint); const body = data ? JSON.stringify(data) : undefined; - const res = await wrapThrowsAsync(fetch)(url.toString(), { method, headers: { "Content-Type": "application/json", + ...(isDebug && { "Cache-Control": "no-cache" }), }, body, }); diff --git a/packages/js-core/src/lib/common/setup.ts b/packages/js-core/src/lib/common/setup.ts index 6e94d0b6e4..93123c365d 100644 --- a/packages/js-core/src/lib/common/setup.ts +++ b/packages/js-core/src/lib/common/setup.ts @@ -179,7 +179,11 @@ export const setup = async ( let environmentState: TEnvironmentState = existingConfig.environment; let userState: TUserState = existingConfig.user; - if (isEnvironmentStateExpired) { + if (isEnvironmentStateExpired || isDebug) { + if (isDebug) { + logger.debug("Debug mode is active, refetching environment state"); + } + const environmentStateResponse = await fetchEnvironmentState({ appUrl: configInput.appUrl, environmentId: configInput.environmentId, @@ -201,10 +205,14 @@ export const setup = async ( } } - if (isUserStateExpired) { + if (isUserStateExpired || isDebug) { // If the existing person state (expired) has a userId, we need to fetch the person state // If the existing person state (expired) has no userId, we need to set the person state to the default + if (isDebug) { + logger.debug("Debug mode is active, refetching user state"); + } + if (userState.data.userId) { const updatesResponse = await sendUpdatesToBackend({ appUrl: configInput.appUrl, @@ -230,7 +238,7 @@ export const setup = async ( responseMessage: "Unknown error", }); } - } else { + } else if (!isDebug) { userState = DEFAULT_USER_STATE_NO_USER_ID; } } @@ -271,7 +279,6 @@ export const setup = async ( throw environmentStateResponse.error; } - // const personState = DEFAULT_USER_STATE_NO_USER_ID; let userState: TUserState = DEFAULT_USER_STATE_NO_USER_ID; if ("userId" in configInput && configInput.userId) { diff --git a/packages/js-core/src/lib/environment/state.ts b/packages/js-core/src/lib/environment/state.ts index ce7a537862..d29c54d146 100644 --- a/packages/js-core/src/lib/environment/state.ts +++ b/packages/js-core/src/lib/environment/state.ts @@ -2,7 +2,7 @@ import { FormbricksAPI } from "@formbricks/api"; import { Config } from "@/lib/common/config"; import { Logger } from "@/lib/common/logger"; -import { filterSurveys } from "@/lib/common/utils"; +import { filterSurveys, getIsDebug } from "@/lib/common/utils"; import type { TConfigInput, TEnvironmentState } from "@/types/config"; import { type ApiErrorResponse, type Result, err, ok } from "@/types/error"; @@ -20,7 +20,7 @@ export const fetchEnvironmentState = async ({ environmentId, }: TConfigInput): Promise> => { const url = `${appUrl}/api/v1/client/${environmentId}/environment`; - const api = new FormbricksAPI({ appUrl, environmentId }); + const api = new FormbricksAPI({ appUrl, environmentId, isDebug: getIsDebug() }); try { const response = await api.client.environment.getState(); diff --git a/packages/js-core/src/lib/environment/tests/state.test.ts b/packages/js-core/src/lib/environment/tests/state.test.ts index cfbb7afbe5..82274a62f7 100644 --- a/packages/js-core/src/lib/environment/tests/state.test.ts +++ b/packages/js-core/src/lib/environment/tests/state.test.ts @@ -37,6 +37,7 @@ vi.mock("@/lib/common/logger", () => ({ // Mock filterSurveys vi.mock("@/lib/common/utils", () => ({ filterSurveys: vi.fn(), + getIsDebug: vi.fn(), })); // Mock Config diff --git a/packages/js-core/src/lib/user/tests/update.test.ts b/packages/js-core/src/lib/user/tests/update.test.ts index 58e3c95148..565492d025 100644 --- a/packages/js-core/src/lib/user/tests/update.test.ts +++ b/packages/js-core/src/lib/user/tests/update.test.ts @@ -30,6 +30,7 @@ vi.mock("@/lib/common/logger", () => ({ vi.mock("@/lib/common/utils", () => ({ filterSurveys: vi.fn(), + getIsDebug: vi.fn(), })); vi.mock("@formbricks/api", () => ({ diff --git a/packages/js-core/src/lib/user/update.ts b/packages/js-core/src/lib/user/update.ts index 4f5a59dc6c..ace4287a99 100644 --- a/packages/js-core/src/lib/user/update.ts +++ b/packages/js-core/src/lib/user/update.ts @@ -2,7 +2,7 @@ import { FormbricksAPI } from "@formbricks/api"; import { Config } from "@/lib/common/config"; import { Logger } from "@/lib/common/logger"; -import { filterSurveys } from "@/lib/common/utils"; +import { filterSurveys, getIsDebug } from "@/lib/common/utils"; import { type TUpdates, type TUserState } from "@/types/config"; import { type ApiErrorResponse, type Result, type ResultError, err, ok, okVoid } from "@/types/error"; @@ -26,7 +26,7 @@ export const sendUpdatesToBackend = async ({ const url = `${appUrl}/api/v1/client/${environmentId}/user`; try { - const api = new FormbricksAPI({ appUrl, environmentId }); + const api = new FormbricksAPI({ appUrl, environmentId, isDebug: getIsDebug() }); const response = await api.client.user.createOrUpdate({ userId: updates.userId,