mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-16 19:15:05 -05:00
feat: use formbricks/api package internally in /js & /lib (#955)
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
committed by
GitHub
parent
7dcadc660f
commit
0115c58364
@@ -1,6 +1,6 @@
|
||||
import { responses } from "@/app/lib/api/response";
|
||||
import { updateDisplay } from "@formbricks/lib/display/service";
|
||||
import { TDisplayInput, ZDisplayUpdate } from "@formbricks/types/displays";
|
||||
import { TDisplayCreateInput, ZDisplayUpdateInput } from "@formbricks/types/displays";
|
||||
import { NextResponse } from "next/server";
|
||||
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||
|
||||
@@ -16,8 +16,8 @@ export async function PUT(
|
||||
if (!displayId) {
|
||||
return responses.badRequestResponse("Missing displayId", undefined, true);
|
||||
}
|
||||
const displayInput: TDisplayInput = await request.json();
|
||||
const inputValidation = ZDisplayUpdate.safeParse(displayInput);
|
||||
const displayInput: TDisplayCreateInput = await request.json();
|
||||
const inputValidation = ZDisplayUpdateInput.safeParse(displayInput);
|
||||
|
||||
if (!inputValidation.success) {
|
||||
return responses.badRequestResponse(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
|
||||
import { createDisplay } from "@formbricks/lib/display/service";
|
||||
import { getSurvey } from "@formbricks/lib/survey/service";
|
||||
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
|
||||
import { TDisplay, ZDisplayInput } from "@formbricks/types/displays";
|
||||
import { TDisplay, ZDisplayCreateInput } from "@formbricks/types/displays";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function OPTIONS(): Promise<NextResponse> {
|
||||
@@ -14,7 +14,7 @@ export async function OPTIONS(): Promise<NextResponse> {
|
||||
|
||||
export async function POST(request: Request): Promise<NextResponse> {
|
||||
const jsonInput: unknown = await request.json();
|
||||
const inputValidation = ZDisplayInput.safeParse(jsonInput);
|
||||
const inputValidation = ZDisplayCreateInput.safeParse(jsonInput);
|
||||
|
||||
if (!inputValidation.success) {
|
||||
return responses.badRequestResponse(
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import ContentWrapper from "@formbricks/ui/ContentWrapper";
|
||||
import { SurveyInline } from "@formbricks/ui/Survey";
|
||||
import SurveyLinkUsed from "@/app/s/[surveyId]/components/SurveyLinkUsed";
|
||||
import VerifyEmail from "@/app/s/[surveyId]/components/VerifyEmail";
|
||||
import { getPrefillResponseData } from "@/app/s/[surveyId]/lib/prefilling";
|
||||
import { createDisplay } from "@formbricks/lib/client/display";
|
||||
import { ResponseQueue } from "@formbricks/lib/responseQueue";
|
||||
import { SurveyState } from "@formbricks/lib/surveyState";
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { TResponse, TResponseData, TResponseUpdate } from "@formbricks/types/responses";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import ContentWrapper from "@formbricks/ui/ContentWrapper";
|
||||
import { SurveyInline } from "@formbricks/ui/Survey";
|
||||
import { ArrowPathIcon } from "@heroicons/react/24/solid";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { FormbricksAPI } from "@formbricks/api";
|
||||
|
||||
interface LinkSurveyProps {
|
||||
survey: TSurvey;
|
||||
@@ -138,7 +138,18 @@ export default function LinkSurvey({
|
||||
formbricksSignature={product.formbricksSignature}
|
||||
onDisplay={async () => {
|
||||
if (!isPreview) {
|
||||
const { id } = await createDisplay({ surveyId: survey.id }, webAppUrl);
|
||||
const api = new FormbricksAPI({
|
||||
apiHost: webAppUrl,
|
||||
environmentId: survey.environmentId,
|
||||
});
|
||||
const res = await api.client.display.create({
|
||||
surveyId: survey.id,
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error("Could not create display");
|
||||
}
|
||||
const { id } = res.data;
|
||||
|
||||
const newSurveyState = surveyState.copy();
|
||||
newSurveyState.updateDisplayId(id);
|
||||
setSurveyState(newSurveyState);
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/types": "workspace:*",
|
||||
"@formbricks/lib": "workspace:*",
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"terser": "^5.22.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Result } from "@formbricks/types/errorHandlers";
|
||||
import { NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/makeRequest";
|
||||
import { TDisplay, TDisplayInput } from "@formbricks/types/displays";
|
||||
import { TDisplay, TDisplayCreateInput, TDisplayUpdateInput } from "@formbricks/types/displays";
|
||||
|
||||
export class DisplayAPI {
|
||||
private apiHost: string;
|
||||
@@ -10,14 +10,14 @@ export class DisplayAPI {
|
||||
this.apiHost = baseUrl;
|
||||
}
|
||||
|
||||
async markDisplayedForPerson({
|
||||
surveyId,
|
||||
personId,
|
||||
}: TDisplayInput): Promise<Result<TDisplay, NetworkError | Error>> {
|
||||
return makeRequest(this.apiHost, "/api/v1/client/displays", "POST", { surveyId, personId });
|
||||
async create(displayInput: TDisplayCreateInput): Promise<Result<TDisplay, NetworkError | Error>> {
|
||||
return makeRequest(this.apiHost, "/api/v1/client/displays", "POST", displayInput);
|
||||
}
|
||||
|
||||
async markResponded({ displayId }: { displayId: string }): Promise<Result<TDisplay, NetworkError | Error>> {
|
||||
return makeRequest(this.apiHost, `/api/v1/client/displays/${displayId}/responded`, "POST");
|
||||
async update(
|
||||
displayId: string,
|
||||
displayInput: TDisplayUpdateInput
|
||||
): Promise<Result<TDisplay, NetworkError | Error>> {
|
||||
return makeRequest(this.apiHost, `/api/v1/client/displays/${displayId}`, "PUT", displayInput);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import type { TDisplay, TDisplayInput } from "../../../types/displays";
|
||||
import type { TJsConfig } from "../../../types/js";
|
||||
import { NetworkError, Result, err, ok, okVoid } from "./errors";
|
||||
|
||||
export const createDisplay = async (
|
||||
displayCreateRequest: TDisplayInput,
|
||||
config: TJsConfig
|
||||
): Promise<Result<TDisplay, NetworkError>> => {
|
||||
const url = `${config.apiHost}/api/v1/client/displays`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(displayCreateRequest),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
return err({
|
||||
code: "network_error",
|
||||
message: "Could not create display",
|
||||
status: res.status,
|
||||
url,
|
||||
responseMessage: await res.text(),
|
||||
});
|
||||
}
|
||||
|
||||
const jsonRes = await res.json();
|
||||
|
||||
return ok(jsonRes.data as TDisplay);
|
||||
};
|
||||
|
||||
export const markDisplayResponded = async (
|
||||
displayId: string,
|
||||
config: TJsConfig
|
||||
): Promise<Result<void, NetworkError>> => {
|
||||
const url = `${config.apiHost}/api/v1/client/displays/${displayId}/responded`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
return err({
|
||||
code: "network_error",
|
||||
message: "Could not mark display as responded",
|
||||
status: res.status,
|
||||
url,
|
||||
responseMessage: await res.text(),
|
||||
});
|
||||
}
|
||||
|
||||
return okVoid();
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
import type { TJsConfig } from "../../../types/js";
|
||||
import type { TResponse, TResponseInput } from "../../../types/responses";
|
||||
import { NetworkError, Result, err, ok } from "./errors";
|
||||
|
||||
export const createResponse = async (
|
||||
responseInput: TResponseInput,
|
||||
config: TJsConfig
|
||||
): Promise<Result<TResponse, NetworkError>> => {
|
||||
const url = `${config.apiHost}/api/v1/client/responses`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(responseInput),
|
||||
});
|
||||
|
||||
const jsonRes = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
return err({
|
||||
code: "network_error",
|
||||
message: "Could not create response",
|
||||
status: res.status,
|
||||
url,
|
||||
responseMessage: jsonRes.message,
|
||||
});
|
||||
}
|
||||
|
||||
return ok(jsonRes.data as TResponse);
|
||||
};
|
||||
|
||||
export const updateResponse = async (
|
||||
responseInput: TResponseInput,
|
||||
responseId: string,
|
||||
config: TJsConfig
|
||||
): Promise<Result<TResponse, NetworkError>> => {
|
||||
const url = `${config.apiHost}/api/v1/client/responses/${responseId}`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(responseInput),
|
||||
});
|
||||
|
||||
const resJson = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
return err({
|
||||
code: "network_error",
|
||||
message: "Could not update response",
|
||||
status: res.status,
|
||||
url,
|
||||
responseMessage: resJson.message,
|
||||
});
|
||||
}
|
||||
|
||||
return ok(resJson.data as TResponse);
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createDisplay } from "@formbricks/lib/client/display";
|
||||
import { ResponseQueue } from "@formbricks/lib/responseQueue";
|
||||
import SurveyState from "@formbricks/lib/surveyState";
|
||||
import { renderSurveyModal } from "@formbricks/surveys";
|
||||
@@ -8,6 +7,7 @@ import { Config } from "./config";
|
||||
import { ErrorHandler } from "./errors";
|
||||
import { Logger } from "./logger";
|
||||
import { sync } from "./sync";
|
||||
import { FormbricksAPI } from "@formbricks/api";
|
||||
|
||||
const containerId = "formbricks-web-container";
|
||||
const config = Config.getInstance();
|
||||
@@ -59,13 +59,19 @@ export const renderWidget = (survey: TSurveyWithTriggers) => {
|
||||
highlightBorderColor,
|
||||
placement,
|
||||
onDisplay: async () => {
|
||||
const { id } = await createDisplay(
|
||||
{
|
||||
surveyId: survey.id,
|
||||
personId: config.get().state.person.id,
|
||||
},
|
||||
config.get().apiHost
|
||||
);
|
||||
const api = new FormbricksAPI({
|
||||
apiHost: config.get().apiHost,
|
||||
environmentId: config.get().environmentId,
|
||||
});
|
||||
const res = await api.client.display.create({
|
||||
surveyId: survey.id,
|
||||
personId: config.get().state.person.id,
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error("Could not create display");
|
||||
}
|
||||
const { id } = res.data;
|
||||
|
||||
surveyState.updateDisplayId(id);
|
||||
responseQueue.updateSurveyState(surveyState);
|
||||
},
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { TDisplay, TDisplayInput } from "@formbricks/types/displays";
|
||||
|
||||
export const createDisplay = async (
|
||||
displayCreateRequest: TDisplayInput,
|
||||
apiHost: string
|
||||
): Promise<TDisplay> => {
|
||||
const res = await fetch(`${apiHost}/api/v1/client/displays`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(displayCreateRequest),
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(res.text);
|
||||
throw new Error("Could not create display");
|
||||
}
|
||||
const resJson = await res.json();
|
||||
return resJson.data;
|
||||
};
|
||||
|
||||
export const updateDisplay = async (displayId: string, displayInput: any, apiHost: string): Promise<void> => {
|
||||
const res = await fetch(`${apiHost}/api/v1/client/displays/${displayId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(displayInput),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error("Could not update display");
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
export const markDisplayResponded = async (displayId: string, apiHost: string): Promise<void> => {
|
||||
const res = await fetch(`${apiHost}/api/v1/client/displays/${displayId}/responded`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error("Could not update display");
|
||||
}
|
||||
return;
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import { TResponse, TResponseInput, TResponseUpdateInput } from "@formbricks/types/responses";
|
||||
|
||||
export const createResponse = async (responseInput: TResponseInput, apiHost: string): Promise<TResponse> => {
|
||||
const res = await fetch(`${apiHost}/api/v1/client/responses`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(responseInput),
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(res.text);
|
||||
throw new Error("Could not create response");
|
||||
}
|
||||
const resJson = await res.json();
|
||||
return resJson.data;
|
||||
};
|
||||
|
||||
export const updateResponse = async (
|
||||
responseInput: TResponseUpdateInput,
|
||||
responseId: string,
|
||||
apiHost: string
|
||||
): Promise<TResponse> => {
|
||||
const res = await fetch(`${apiHost}/api/v1/client/responses/${responseId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(responseInput),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error("Could not update response");
|
||||
}
|
||||
const resJson = await res.json();
|
||||
return resJson.data;
|
||||
};
|
||||
@@ -2,7 +2,14 @@ import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber } from "@formbricks/types/common";
|
||||
import { TDisplay, TDisplayInput, TDisplaysWithSurveyName, ZDisplayInput } from "@formbricks/types/displays";
|
||||
import {
|
||||
TDisplay,
|
||||
TDisplayCreateInput,
|
||||
TDisplayUpdateInput,
|
||||
TDisplaysWithSurveyName,
|
||||
ZDisplayCreateInput,
|
||||
ZDisplayUpdateInput,
|
||||
} from "@formbricks/types/displays";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { Prisma } from "@prisma/client";
|
||||
@@ -42,9 +49,9 @@ const selectDisplay = {
|
||||
|
||||
export const updateDisplay = async (
|
||||
displayId: string,
|
||||
displayInput: Partial<TDisplayInput>
|
||||
displayInput: Partial<TDisplayUpdateInput>
|
||||
): Promise<TDisplay> => {
|
||||
validateInputs([displayInput, ZDisplayInput.partial()]);
|
||||
validateInputs([displayInput, ZDisplayUpdateInput.partial()]);
|
||||
try {
|
||||
const displayPrisma = await prisma.display.update({
|
||||
where: {
|
||||
@@ -74,9 +81,8 @@ export const updateDisplay = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const createDisplay = async (displayInput: TDisplayInput): Promise<TDisplay> => {
|
||||
validateInputs([displayInput, ZDisplayInput]);
|
||||
|
||||
export const createDisplay = async (displayInput: TDisplayCreateInput): Promise<TDisplay> => {
|
||||
validateInputs([displayInput, ZDisplayCreateInput]);
|
||||
try {
|
||||
const displayPrisma = await prisma.display.create({
|
||||
data: {
|
||||
@@ -271,7 +277,7 @@ export const getDisplayCountBySurveyId = async (surveyId: string): Promise<numbe
|
||||
validateInputs([surveyId, ZId]);
|
||||
|
||||
try {
|
||||
const displayCount = await prisma.response.count({
|
||||
const displayCount = await prisma.display.count({
|
||||
where: {
|
||||
surveyId: surveyId,
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"lint:report": "eslint . --format json --output-file ../../lint-results/app-store.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/api": "*",
|
||||
"@aws-sdk/client-s3": "^3.429.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.429.0",
|
||||
"mime": "3.0.0",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { FormbricksAPI } from "@formbricks/api";
|
||||
import { TResponseUpdate } from "@formbricks/types/responses";
|
||||
import { createResponse, updateResponse } from "./client/response";
|
||||
import { updateDisplay } from "./client/display";
|
||||
import SurveyState from "./surveyState";
|
||||
|
||||
interface QueueConfig {
|
||||
@@ -16,10 +15,15 @@ export class ResponseQueue {
|
||||
private config: QueueConfig;
|
||||
private surveyState: SurveyState;
|
||||
private isRequestInProgress = false;
|
||||
private api: FormbricksAPI;
|
||||
|
||||
constructor(config: QueueConfig, surveyState: SurveyState) {
|
||||
this.config = config;
|
||||
this.surveyState = surveyState;
|
||||
this.api = new FormbricksAPI({
|
||||
apiHost: config.apiHost,
|
||||
environmentId: "",
|
||||
});
|
||||
}
|
||||
|
||||
add(responseUpdate: TResponseUpdate) {
|
||||
@@ -69,21 +73,21 @@ export class ResponseQueue {
|
||||
async sendResponse(responseUpdate: TResponseUpdate): Promise<boolean> {
|
||||
try {
|
||||
if (this.surveyState.responseId !== null) {
|
||||
await updateResponse(responseUpdate, this.surveyState.responseId, this.config.apiHost);
|
||||
await this.api.client.response.update({ ...responseUpdate, responseId: this.surveyState.responseId });
|
||||
} else {
|
||||
const response = await createResponse(
|
||||
{
|
||||
...responseUpdate,
|
||||
surveyId: this.surveyState.surveyId,
|
||||
personId: this.config.personId || null,
|
||||
singleUseId: this.surveyState.singleUseId || null,
|
||||
},
|
||||
this.config.apiHost
|
||||
);
|
||||
if (this.surveyState.displayId) {
|
||||
await updateDisplay(this.surveyState.displayId, { responseId: response.id }, this.config.apiHost);
|
||||
const response = await this.api.client.response.create({
|
||||
...responseUpdate,
|
||||
surveyId: this.surveyState.surveyId,
|
||||
personId: this.config.personId || null,
|
||||
singleUseId: this.surveyState.singleUseId || null,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Could not create response");
|
||||
}
|
||||
this.surveyState.updateResponseId(response.id);
|
||||
if (this.surveyState.displayId) {
|
||||
await this.api.client.display.update(this.surveyState.displayId, { responseId: response.data.id });
|
||||
}
|
||||
this.surveyState.updateResponseId(response.data.id);
|
||||
if (this.config.setSurveyState) {
|
||||
this.config.setSurveyState(this.surveyState);
|
||||
}
|
||||
|
||||
@@ -13,17 +13,19 @@ export const ZDisplay = z.object({
|
||||
|
||||
export type TDisplay = z.infer<typeof ZDisplay>;
|
||||
|
||||
export const ZDisplayInput = z.object({
|
||||
export const ZDisplayCreateInput = z.object({
|
||||
surveyId: z.string().cuid2(),
|
||||
personId: z.string().cuid2().optional(),
|
||||
responseId: z.string().cuid2().optional(),
|
||||
});
|
||||
|
||||
export const ZDisplayUpdate = z.object({
|
||||
export type TDisplayCreateInput = z.infer<typeof ZDisplayCreateInput>;
|
||||
|
||||
export const ZDisplayUpdateInput = z.object({
|
||||
responseId: z.string().cuid2().optional(),
|
||||
});
|
||||
|
||||
export type TDisplayInput = z.infer<typeof ZDisplayInput>;
|
||||
export type TDisplayUpdateInput = z.infer<typeof ZDisplayUpdateInput>;
|
||||
|
||||
export const ZDisplaysWithSurveyName = ZDisplay.extend({
|
||||
surveyName: z.string(),
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -432,9 +432,6 @@ importers:
|
||||
|
||||
packages/api:
|
||||
devDependencies:
|
||||
'@formbricks/lib':
|
||||
specifier: workspace:*
|
||||
version: link:../lib
|
||||
'@formbricks/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:../tsconfig
|
||||
@@ -611,6 +608,9 @@ importers:
|
||||
'@aws-sdk/s3-request-presigner':
|
||||
specifier: ^3.429.0
|
||||
version: 3.429.0
|
||||
'@formbricks/api':
|
||||
specifier: '*'
|
||||
version: link:../api
|
||||
'@formbricks/database':
|
||||
specifier: '*'
|
||||
version: link:../database
|
||||
|
||||
Reference in New Issue
Block a user