mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-29 18:00:26 -06:00
chore: refactor services with Formbricks best practices (#988)
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { AuthenticationError, AuthorizationError, ResourceNotFoundError } from "
|
||||
import { Team } from "@prisma/client";
|
||||
import { Prisma as prismaClient } from "@prisma/client/";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getActionClasses } from "@formbricks/lib/actionClass/service";
|
||||
|
||||
export const createShortUrlAction = async (url: string) => {
|
||||
const session = await getServerSession(authOptions);
|
||||
@@ -62,6 +63,8 @@ export async function duplicateSurveyAction(environmentId: string, surveyId: str
|
||||
throw new ResourceNotFoundError("Survey", surveyId);
|
||||
}
|
||||
|
||||
const actionClasses = await getActionClasses(environmentId);
|
||||
|
||||
// create new survey with the data of the existing survey
|
||||
const newSurvey = await prisma.survey.create({
|
||||
data: {
|
||||
@@ -74,7 +77,7 @@ export async function duplicateSurveyAction(environmentId: string, surveyId: str
|
||||
thankYouCard: JSON.parse(JSON.stringify(existingSurvey.thankYouCard)),
|
||||
triggers: {
|
||||
create: existingSurvey.triggers.map((trigger) => ({
|
||||
eventClassId: trigger.id,
|
||||
eventClassId: actionClasses.find((actionClass) => actionClass.name === trigger)!.id,
|
||||
})),
|
||||
},
|
||||
attributeFilters: {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
TGoogleSheetIntegration,
|
||||
TGoogleSheetsConfigData,
|
||||
TGoogleSpreadsheet,
|
||||
TIntegrationInput,
|
||||
} from "@formbricks/types/v1/integrations";
|
||||
import { Button, Checkbox, Label } from "@formbricks/ui";
|
||||
import GoogleSheetLogo from "@/images/google-sheets-small.png";
|
||||
@@ -52,7 +53,7 @@ export default function AddIntegrationModal({
|
||||
const [selectedSpreadsheet, setSelectedSpreadsheet] = useState<any>(null);
|
||||
const [isDeleting, setIsDeleting] = useState<any>(null);
|
||||
const existingIntegrationData = googleSheetIntegration?.config?.data;
|
||||
const googleSheetIntegrationData: Partial<TGoogleSheetIntegration> = {
|
||||
const googleSheetIntegrationData: TIntegrationInput = {
|
||||
type: "googleSheets",
|
||||
config: {
|
||||
key: googleSheetIntegration?.config?.key,
|
||||
|
||||
@@ -4,21 +4,12 @@ import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getSpreadSheets } from "@formbricks/lib/googleSheet/service";
|
||||
import { createOrUpdateIntegration, deleteIntegration } from "@formbricks/lib/integration/service";
|
||||
import { TGoogleSheetIntegration } from "@formbricks/types/v1/integrations";
|
||||
import { TIntegrationInput } from "@formbricks/types/v1/integrations";
|
||||
import { canUserAccessIntegration } from "@formbricks/lib/integration/auth";
|
||||
import { AuthorizationError } from "@formbricks/types/v1/errors";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
|
||||
export async function upsertIntegrationAction(
|
||||
environmentId: string,
|
||||
integrationData: Partial<TGoogleSheetIntegration>
|
||||
) {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session) throw new AuthorizationError("Not authorized");
|
||||
|
||||
const isAuthorized = await hasUserEnvironmentAccess(session.user.id, environmentId);
|
||||
if (!isAuthorized) throw new AuthorizationError("Not authorized");
|
||||
|
||||
export async function upsertIntegrationAction(environmentId: string, integrationData: TIntegrationInput) {
|
||||
return await createOrUpdateIntegration(environmentId, integrationData);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ export const revalidate = REVALIDATION_INTERVAL;
|
||||
|
||||
import EmptySpaceFiller from "@/components/shared/EmptySpaceFiller";
|
||||
import { truncateMiddle } from "@/lib/utils";
|
||||
import { PEOPLE_PER_PAGE, REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { ITEMS_PER_PAGE, REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getPeople, getPeopleCount } from "@formbricks/lib/person/service";
|
||||
import { TPerson } from "@formbricks/types/v1/people";
|
||||
@@ -27,7 +27,7 @@ export default async function PeoplePage({
|
||||
if (!environment) {
|
||||
throw new Error("Environment not found");
|
||||
}
|
||||
const maxPageNumber = Math.ceil(totalPeople / PEOPLE_PER_PAGE);
|
||||
const maxPageNumber = Math.ceil(totalPeople / ITEMS_PER_PAGE);
|
||||
let hidePagination = false;
|
||||
|
||||
let people: TPerson[] = [];
|
||||
@@ -94,7 +94,7 @@ export default async function PeoplePage({
|
||||
baseUrl={`/environments/${params.environmentId}/people`}
|
||||
currentPage={pageNumber}
|
||||
totalItems={totalPeople}
|
||||
itemsPerPage={PEOPLE_PER_PAGE}
|
||||
itemsPerPage={ITEMS_PER_PAGE}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createInviteToken } from "@formbricks/lib/jwt";
|
||||
import { AuthenticationError, AuthorizationError, ValidationError } from "@formbricks/types/v1/errors";
|
||||
import {
|
||||
deleteInvite,
|
||||
getInviteToken,
|
||||
getInvite,
|
||||
inviteUser,
|
||||
resendInvite,
|
||||
updateInvite,
|
||||
@@ -136,7 +136,7 @@ export const leaveTeamAction = async (teamId: string) => {
|
||||
};
|
||||
|
||||
export const createInviteTokenAction = async (inviteId: string) => {
|
||||
const { email } = await getInviteToken(inviteId);
|
||||
const { email } = await getInvite(inviteId);
|
||||
|
||||
const inviteToken = createInviteToken(inviteId, email, {
|
||||
expiresIn: "7d",
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function SurveyStarter({
|
||||
...template.preset,
|
||||
type: surveyType,
|
||||
autoComplete,
|
||||
} as Partial<TSurveyInput>;
|
||||
} as TSurveyInput;
|
||||
try {
|
||||
const survey = await createSurveyAction(environmentId, augmentedTemplate);
|
||||
router.push(`/environments/${environmentId}/surveys/${survey.id}/edit`);
|
||||
|
||||
@@ -308,7 +308,7 @@ export default function SurveyMenuBar({
|
||||
disabled={
|
||||
localSurvey.type === "web" &&
|
||||
localSurvey.triggers &&
|
||||
(localSurvey.triggers[0]?.id === "" || localSurvey.triggers.length === 0)
|
||||
(localSurvey.triggers[0] === "" || localSurvey.triggers.length === 0)
|
||||
}
|
||||
variant="darkCTA"
|
||||
loading={isMutatingSurvey}
|
||||
|
||||
@@ -16,10 +16,9 @@ import {
|
||||
} from "@formbricks/ui";
|
||||
import { CheckCircleIcon, PlusIcon, TrashIcon } from "@heroicons/react/24/solid";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { TSurveyWithAnalytics } from "@formbricks/types/v1/surveys";
|
||||
import { TActionClass } from "@formbricks/types/v1/actionClasses";
|
||||
|
||||
interface WhenToSendCardProps {
|
||||
localSurvey: TSurveyWithAnalytics;
|
||||
setLocalSurvey: (survey: TSurveyWithAnalytics) => void;
|
||||
@@ -40,32 +39,22 @@ export default function WhenToSendCard({
|
||||
|
||||
const autoClose = localSurvey.autoClose !== null;
|
||||
|
||||
let newTrigger = useMemo(
|
||||
() => ({
|
||||
id: "", // Set the appropriate value for the id
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
name: "",
|
||||
type: "code" as const, // Set the appropriate value for the type
|
||||
environmentId: "",
|
||||
description: null,
|
||||
noCodeConfig: null,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const addTriggerEvent = useCallback(() => {
|
||||
const updatedSurvey = { ...localSurvey };
|
||||
updatedSurvey.triggers = [...localSurvey.triggers, newTrigger];
|
||||
updatedSurvey.triggers = [...localSurvey.triggers, ""];
|
||||
setLocalSurvey(updatedSurvey);
|
||||
}, [newTrigger, localSurvey, setLocalSurvey]);
|
||||
}, [localSurvey, setLocalSurvey]);
|
||||
|
||||
const setTriggerEvent = useCallback(
|
||||
(idx: number, actionClassId: string) => {
|
||||
(idx: number, actionClassName: string) => {
|
||||
const updatedSurvey = { ...localSurvey };
|
||||
updatedSurvey.triggers[idx] = actionClassArray!.find((actionClass) => {
|
||||
return actionClass.id === actionClassId;
|
||||
})!;
|
||||
const newActionClass = actionClassArray!.find((actionClass) => {
|
||||
return actionClass.name === actionClassName;
|
||||
});
|
||||
if (!newActionClass) {
|
||||
throw new Error("Action class not found");
|
||||
}
|
||||
updatedSurvey.triggers[idx] = newActionClass.name;
|
||||
setLocalSurvey(updatedSurvey);
|
||||
},
|
||||
[actionClassArray, localSurvey, setLocalSurvey]
|
||||
@@ -104,11 +93,11 @@ export default function WhenToSendCard({
|
||||
|
||||
useEffect(() => {
|
||||
if (activeIndex !== null) {
|
||||
const newActionClassId = actionClassArray[actionClassArray.length - 1].id;
|
||||
const currentActionClassId = localSurvey.triggers[activeIndex]?.id;
|
||||
const newActionClass = actionClassArray[actionClassArray.length - 1].name;
|
||||
const currentActionClass = localSurvey.triggers[activeIndex];
|
||||
|
||||
if (newActionClassId !== currentActionClassId) {
|
||||
setTriggerEvent(activeIndex, newActionClassId);
|
||||
if (newActionClass !== currentActionClass) {
|
||||
setTriggerEvent(activeIndex, newActionClass);
|
||||
}
|
||||
|
||||
setActiveIndex(null);
|
||||
@@ -148,7 +137,7 @@ export default function WhenToSendCard({
|
||||
)}>
|
||||
<div className="inline-flex px-4 py-4">
|
||||
<div className="flex items-center pl-2 pr-5">
|
||||
{!localSurvey.triggers || localSurvey.triggers.length === 0 || !localSurvey.triggers[0]?.id ? (
|
||||
{!localSurvey.triggers || localSurvey.triggers.length === 0 || !localSurvey.triggers[0] ? (
|
||||
<div
|
||||
className={cn(
|
||||
localSurvey.type !== "link"
|
||||
@@ -186,8 +175,8 @@ export default function WhenToSendCard({
|
||||
<div className="inline-flex items-center">
|
||||
<p className="mr-2 w-14 text-right text-sm">{idx === 0 ? "When" : "or"}</p>
|
||||
<Select
|
||||
value={triggerEventClass.id}
|
||||
onValueChange={(actionClassId) => setTriggerEvent(idx, actionClassId)}>
|
||||
value={triggerEventClass}
|
||||
onValueChange={(actionClassName) => setTriggerEvent(idx, actionClassName)}>
|
||||
<SelectTrigger className="w-[240px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
@@ -205,8 +194,8 @@ export default function WhenToSendCard({
|
||||
<SelectSeparator />
|
||||
{actionClassArray.map((actionClass) => (
|
||||
<SelectItem
|
||||
value={actionClass.id}
|
||||
key={actionClass.id}
|
||||
value={actionClass.name}
|
||||
key={actionClass.name}
|
||||
title={actionClass.description ? actionClass.description : ""}>
|
||||
{actionClass.name}
|
||||
</SelectItem>
|
||||
|
||||
@@ -14,6 +14,13 @@ export async function updateSurveyAction(survey: TSurvey): Promise<TSurvey> {
|
||||
const isAuthorized = await canUserAccessSurvey(session.user.id, survey.id);
|
||||
if (!isAuthorized) throw new AuthorizationError("Not authorized");
|
||||
|
||||
if (typeof survey.createdAt === "string") {
|
||||
survey.createdAt = new Date(survey.createdAt);
|
||||
}
|
||||
if (typeof survey.updatedAt === "string") {
|
||||
survey.updatedAt = new Date(survey.updatedAt);
|
||||
}
|
||||
|
||||
return await updateSurvey(survey);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { getServerSession } from "next-auth";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
import { createSurvey } from "@formbricks/lib/survey/service";
|
||||
import { AuthorizationError } from "@formbricks/types/v1/errors";
|
||||
import { TSurvey } from "@formbricks/types/v1/surveys";
|
||||
import { TSurveyInput } from "@formbricks/types/v1/surveys";
|
||||
|
||||
export async function createSurveyAction(environmentId: string, surveyBody: Partial<TSurvey>) {
|
||||
export async function createSurveyAction(environmentId: string, surveyBody: TSurveyInput) {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session) throw new AuthorizationError("Not authorized");
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function TemplateList({
|
||||
...activeTemplate.preset,
|
||||
type: surveyType,
|
||||
autoComplete,
|
||||
} as Partial<TSurveyInput>;
|
||||
} as TSurveyInput;
|
||||
const survey = await createSurveyAction(environmentId, augmentedTemplate);
|
||||
router.push(`/environments/${environmentId}/surveys/${survey.id}/edit`);
|
||||
};
|
||||
|
||||
@@ -5,9 +5,9 @@ import { authOptions } from "@formbricks/lib/authOptions";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
import { AuthorizationError } from "@formbricks/types/v1/errors";
|
||||
import { TSurvey } from "@formbricks/types/v1/surveys";
|
||||
import { TSurveyInput } from "@formbricks/types/v1/surveys";
|
||||
|
||||
export async function createSurveyAction(environmentId: string, surveyBody: Partial<TSurvey>) {
|
||||
export async function createSurveyAction(environmentId: string, surveyBody: TSurveyInput) {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session) throw new AuthorizationError("Not authorized");
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ export async function POST(request: Request): Promise<NextResponse> {
|
||||
// find teamId & teamOwnerId from environmentId
|
||||
const teamDetails = await getTeamDetails(survey.environmentId);
|
||||
|
||||
console.log("teamDetails", teamDetails);
|
||||
|
||||
// create display
|
||||
let display: TDisplay;
|
||||
try {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/lib/api/validator";
|
||||
import { createAction } from "@formbricks/lib/action/service";
|
||||
import { ZJsActionInput } from "@formbricks/types/v1/js";
|
||||
import { ZActionInput } from "@formbricks/types/v1/actions";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function OPTIONS(): Promise<NextResponse> {
|
||||
@@ -13,7 +13,7 @@ export async function POST(req: Request): Promise<NextResponse> {
|
||||
const jsonInput = await req.json();
|
||||
|
||||
// validate using zod
|
||||
const inputValidation = ZJsActionInput.safeParse(jsonInput);
|
||||
const inputValidation = ZActionInput.safeParse(jsonInput);
|
||||
|
||||
if (!inputValidation.success) {
|
||||
return responses.badRequestResponse(
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function POST(req: Request, { params }): Promise<NextResponse> {
|
||||
|
||||
let returnedPerson;
|
||||
// check if person with this userId exists
|
||||
const existingPerson = await prisma.person.findFirst({
|
||||
const person = await prisma.person.findFirst({
|
||||
where: {
|
||||
environmentId,
|
||||
attributes: {
|
||||
@@ -46,7 +46,7 @@ export async function POST(req: Request, { params }): Promise<NextResponse> {
|
||||
select: selectPerson,
|
||||
});
|
||||
// if person exists, reconnect session and delete old user
|
||||
if (existingPerson) {
|
||||
if (person) {
|
||||
// reconnect session to new person
|
||||
await prisma.session.update({
|
||||
where: {
|
||||
@@ -55,7 +55,7 @@ export async function POST(req: Request, { params }): Promise<NextResponse> {
|
||||
data: {
|
||||
person: {
|
||||
connect: {
|
||||
id: existingPerson.id,
|
||||
id: person.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -64,7 +64,7 @@ export async function POST(req: Request, { params }): Promise<NextResponse> {
|
||||
// delete old person
|
||||
await deletePerson(personId);
|
||||
|
||||
returnedPerson = existingPerson;
|
||||
returnedPerson = person;
|
||||
} else {
|
||||
// update person with userId
|
||||
returnedPerson = await prisma.person.update({
|
||||
@@ -90,14 +90,14 @@ export async function POST(req: Request, { params }): Promise<NextResponse> {
|
||||
});
|
||||
}
|
||||
|
||||
const person = transformPrismaPerson(returnedPerson);
|
||||
const transformedPerson = transformPrismaPerson(returnedPerson);
|
||||
|
||||
if (person) {
|
||||
if (transformedPerson) {
|
||||
// revalidate person
|
||||
revalidateTag(person.id);
|
||||
revalidateTag(transformedPerson.id);
|
||||
}
|
||||
|
||||
const state = await getUpdatedState(environmentId, person.id, sessionId);
|
||||
const state = await getUpdatedState(environmentId, transformedPerson.id, sessionId);
|
||||
|
||||
return responses.successResponse({ ...state }, true);
|
||||
} catch (error) {
|
||||
|
||||
@@ -165,7 +165,7 @@ export const getSurveys = async (environmentId: string, person: TPerson): Promis
|
||||
.map((survey) => ({
|
||||
...survey,
|
||||
singleUse: survey.singleUse ? JSON.parse(JSON.stringify(survey.singleUse)) : null,
|
||||
triggers: survey.triggers.map((trigger) => trigger.eventClass),
|
||||
triggers: survey.triggers.map((trigger) => trigger.eventClass.name),
|
||||
attributeFilters: survey.attributeFilters.map((af) => ({
|
||||
...af,
|
||||
attributeClassId: af.attributeClass.id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { NextResponse } from "next/server";
|
||||
import { getSurvey, updateSurvey, deleteSurvey } from "@formbricks/lib/survey/service";
|
||||
import { TSurvey, ZSurveyInput } from "@formbricks/types/v1/surveys";
|
||||
import { TSurvey, ZSurvey } from "@formbricks/types/v1/surveys";
|
||||
import { transformErrorToDetails } from "@/lib/api/validator";
|
||||
import { authenticateRequest } from "@/app/api/v1/auth";
|
||||
import { handleErrorResponse } from "@/app/api/v1/auth";
|
||||
@@ -64,7 +64,7 @@ export async function PUT(
|
||||
return responses.notFoundResponse("Survey", params.surveyId);
|
||||
}
|
||||
const surveyUpdate = await request.json();
|
||||
const inputValidation = ZSurveyInput.safeParse(surveyUpdate);
|
||||
const inputValidation = ZSurvey.safeParse(surveyUpdate);
|
||||
if (!inputValidation.success) {
|
||||
return responses.badRequestResponse(
|
||||
"Fields are missing or incorrectly formatted",
|
||||
|
||||
@@ -28,7 +28,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
if (!sessionId) {
|
||||
return res.status(400).json({ message: "Missing sessionId" });
|
||||
}
|
||||
let returnedPerson;
|
||||
let person;
|
||||
// check if person exists
|
||||
const existingPerson = await prisma.person.findFirst({
|
||||
where: {
|
||||
@@ -81,10 +81,10 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
id: personId,
|
||||
},
|
||||
});
|
||||
returnedPerson = existingPerson;
|
||||
person = existingPerson;
|
||||
} else {
|
||||
// update person
|
||||
returnedPerson = await prisma.person.update({
|
||||
person = await prisma.person.update({
|
||||
where: {
|
||||
id: personId,
|
||||
},
|
||||
@@ -122,10 +122,10 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
}
|
||||
|
||||
const settings = await getSettings(environmentId, returnedPerson.id);
|
||||
const settings = await getSettings(environmentId, person.id);
|
||||
|
||||
// return updated person and settings
|
||||
return res.json({ person: returnedPerson, settings });
|
||||
return res.json({ person, settings });
|
||||
}
|
||||
|
||||
// Unknown HTTP Method
|
||||
|
||||
@@ -99,19 +99,19 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
// POST
|
||||
else if (req.method === "PUT") {
|
||||
const data = { ...req.body, updatedAt: new Date() };
|
||||
const prismaRes = await prisma.person.update({
|
||||
const person = await prisma.person.update({
|
||||
where: { id: personId },
|
||||
data,
|
||||
});
|
||||
return res.json(prismaRes);
|
||||
return res.json(person);
|
||||
}
|
||||
|
||||
// Delete
|
||||
else if (req.method === "DELETE") {
|
||||
const prismaRes = await prisma.person.delete({
|
||||
const person = await prisma.person.delete({
|
||||
where: { id: personId },
|
||||
});
|
||||
return res.json(prismaRes);
|
||||
return res.json(person);
|
||||
}
|
||||
|
||||
// Unknown HTTP Method
|
||||
|
||||
@@ -4,25 +4,26 @@ import { prisma } from "@formbricks/database";
|
||||
import { TAction } from "@formbricks/types/v1/actions";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { TJsActionInput } from "@formbricks/types/v1/js";
|
||||
import { EventType, Prisma } from "@prisma/client";
|
||||
import { revalidateTag, unstable_cache } from "next/cache";
|
||||
import { cache } from "react";
|
||||
import z from "zod";
|
||||
import { getActionClassCacheTag } from "../actionClass/service";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { SERVICES_REVALIDATION_INTERVAL, ITEMS_PER_PAGE } from "../constants";
|
||||
import { getSessionCached } from "../session/service";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { TActionInput, ZActionInput } from "@formbricks/types/v1/actions";
|
||||
import { ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
|
||||
export const getActionsCacheTag = (environmentId: string): string => `environments-${environmentId}-actions`;
|
||||
|
||||
export const getActionsByEnvironmentId = async (
|
||||
environmentId: string,
|
||||
limit?: number
|
||||
limit?: number,
|
||||
page?: number
|
||||
): Promise<TAction[]> => {
|
||||
const actions = await unstable_cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [limit, z.number().optional()]);
|
||||
validateInputs([environmentId, ZId], [limit, ZOptionalNumber], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const actionsPrisma = await prisma.event.findMany({
|
||||
where: {
|
||||
@@ -33,7 +34,8 @@ export const getActionsByEnvironmentId = async (
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
take: limit ? limit : 20,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
include: {
|
||||
eventClass: true,
|
||||
},
|
||||
@@ -72,7 +74,8 @@ export const getActionsByEnvironmentId = async (
|
||||
}));
|
||||
};
|
||||
|
||||
export const createAction = async (data: TJsActionInput): Promise<TAction> => {
|
||||
export const createAction = async (data: TActionInput): Promise<TAction> => {
|
||||
validateInputs([data, ZActionInput]);
|
||||
const { environmentId, name, properties, sessionId } = data;
|
||||
|
||||
let eventType: EventType = EventType.code;
|
||||
@@ -133,7 +136,8 @@ export const createAction = async (data: TJsActionInput): Promise<TAction> => {
|
||||
};
|
||||
};
|
||||
|
||||
export const getActionCountInLastHour = cache(async (actionClassId: string) => {
|
||||
export const getActionCountInLastHour = async (actionClassId: string): Promise<number> => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
try {
|
||||
const numEventsLastHour = await prisma.event.count({
|
||||
where: {
|
||||
@@ -147,9 +151,10 @@ export const getActionCountInLastHour = cache(async (actionClassId: string) => {
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getActionCountInLast24Hours = cache(async (actionClassId: string) => {
|
||||
export const getActionCountInLast24Hours = async (actionClassId: string): Promise<number> => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
try {
|
||||
const numEventsLast24Hours = await prisma.event.count({
|
||||
where: {
|
||||
@@ -163,9 +168,10 @@ export const getActionCountInLast24Hours = cache(async (actionClassId: string) =
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getActionCountInLast7Days = cache(async (actionClassId: string) => {
|
||||
export const getActionCountInLast7Days = async (actionClassId: string): Promise<number> => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
try {
|
||||
const numEventsLast7Days = await prisma.event.count({
|
||||
where: {
|
||||
@@ -179,4 +185,4 @@ export const getActionCountInLast7Days = cache(async (actionClassId: string) =>
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { SERVICES_REVALIDATION_INTERVAL, ITEMS_PER_PAGE } from "../constants";
|
||||
import { TActionClass, TActionClassInput, ZActionClassInput } from "@formbricks/types/v1/actionClasses";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/v1/common";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { revalidateTag, unstable_cache } from "next/cache";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
@@ -26,16 +27,18 @@ const select = {
|
||||
environmentId: true,
|
||||
};
|
||||
|
||||
export const getActionClasses = (environmentId: string): Promise<TActionClass[]> =>
|
||||
export const getActionClasses = (environmentId: string, page?: number): Promise<TActionClass[]> =>
|
||||
unstable_cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
try {
|
||||
let actionClasses = await prisma.eventClass.findMany({
|
||||
const actionClasses = await prisma.eventClass.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
select,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
orderBy: {
|
||||
createdAt: "asc",
|
||||
},
|
||||
@@ -156,7 +159,8 @@ export const updateActionClass = async (
|
||||
|
||||
export const getActionClassCached = async (name: string, environmentId: string) =>
|
||||
unstable_cache(
|
||||
async () => {
|
||||
async (): Promise<TActionClass | null> => {
|
||||
validateInputs([name, ZString], [environmentId, ZId]);
|
||||
return await prisma.eventClass.findFirst({
|
||||
where: {
|
||||
name,
|
||||
|
||||
@@ -4,10 +4,9 @@ import { prisma } from "@formbricks/database";
|
||||
import { TActivityFeedItem } from "@formbricks/types/v1/activity";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { cache } from "react";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
|
||||
export const getActivityTimeline = cache(async (personId: string): Promise<TActivityFeedItem[]> => {
|
||||
export const getActivityTimeline = async (personId: string): Promise<TActivityFeedItem[]> => {
|
||||
validateInputs([personId, ZId]);
|
||||
const person = await prisma.person.findUnique({
|
||||
where: {
|
||||
@@ -83,4 +82,4 @@ export const getActivityTimeline = cache(async (personId: string): Promise<TActi
|
||||
const unifiedList: TActivityFeedItem[] = [...unifiedAttributes, ...unifiedDisplays, ...unifiedEvents];
|
||||
|
||||
return unifiedList;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import "server-only";
|
||||
|
||||
import z from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TApiKey, TApiKeyCreateInput, ZApiKeyCreateInput } from "@formbricks/types/v1/apiKeys";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { getHash } from "../crypto";
|
||||
import { createHash, randomBytes } from "crypto";
|
||||
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { ZString, ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
|
||||
export const getApiKey = async (apiKeyId: string): Promise<TApiKey | null> => {
|
||||
validateInputs([apiKeyId, z.string()]);
|
||||
validateInputs([apiKeyId, ZString]);
|
||||
if (!apiKeyId) {
|
||||
throw new InvalidInputError("API key cannot be null or undefined.");
|
||||
}
|
||||
@@ -38,13 +38,16 @@ export const getApiKey = async (apiKeyId: string): Promise<TApiKey | null> => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getApiKeys = cache(async (environmentId: string): Promise<TApiKey[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getApiKeys = async (environmentId: string, page?: number): Promise<TApiKey[]> => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const apiKeys = await prisma.apiKey.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
return apiKeys;
|
||||
@@ -54,7 +57,7 @@ export const getApiKeys = cache(async (environmentId: string): Promise<TApiKey[]
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const hashApiKey = (key: string): string => createHash("sha256").update(key).digest("hex");
|
||||
|
||||
@@ -82,7 +85,7 @@ export async function createApiKey(environmentId: string, apiKeyData: TApiKeyCre
|
||||
}
|
||||
|
||||
export const getApiKeyFromKey = async (apiKey: string): Promise<TApiKey | null> => {
|
||||
validateInputs([apiKey, z.string()]);
|
||||
validateInputs([apiKey, ZString]);
|
||||
if (!apiKey) {
|
||||
throw new InvalidInputError("API key cannot be null or undefined.");
|
||||
}
|
||||
@@ -104,14 +107,16 @@ export const getApiKeyFromKey = async (apiKey: string): Promise<TApiKey | null>
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteApiKey = async (id: string): Promise<void> => {
|
||||
export const deleteApiKey = async (id: string): Promise<TApiKey | null> => {
|
||||
validateInputs([id, ZId]);
|
||||
try {
|
||||
await prisma.apiKey.delete({
|
||||
const deletedApiKeyData = await prisma.apiKey.delete({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
|
||||
return deletedApiKeyData;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
|
||||
@@ -11,9 +11,9 @@ import {
|
||||
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";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { SERVICES_REVALIDATION_INTERVAL, ITEMS_PER_PAGE } from "../constants";
|
||||
import { ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
|
||||
const attributeClassesCacheTag = (environmentId: string): string =>
|
||||
`environments-${environmentId}-attributeClasses`;
|
||||
@@ -22,19 +22,7 @@ const getAttributeClassesCacheKey = (environmentId: string): string[] => [
|
||||
attributeClassesCacheTag(environmentId),
|
||||
];
|
||||
|
||||
export const transformPrismaAttributeClass = (attributeClass: any): TAttributeClass | null => {
|
||||
if (attributeClass === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const transformedAttributeClass: TAttributeClass = {
|
||||
...attributeClass,
|
||||
};
|
||||
|
||||
return transformedAttributeClass;
|
||||
};
|
||||
|
||||
export const getAttributeClass = cache(async (attributeClassId: string): Promise<TAttributeClass | null> => {
|
||||
export const getAttributeClass = async (attributeClassId: string): Promise<TAttributeClass | null> => {
|
||||
validateInputs([attributeClassId, ZId]);
|
||||
try {
|
||||
const attributeClass = await prisma.attributeClass.findFirst({
|
||||
@@ -42,32 +30,35 @@ export const getAttributeClass = cache(async (attributeClassId: string): Promise
|
||||
id: attributeClassId,
|
||||
},
|
||||
});
|
||||
return transformPrismaAttributeClass(attributeClass);
|
||||
return attributeClass;
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching attributeClass with id ${attributeClassId}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getAttributeClasses = async (
|
||||
environmentId: string,
|
||||
page?: number
|
||||
): Promise<TAttributeClass[]> => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
export const getAttributeClasses = cache(async (environmentId: string): Promise<TAttributeClass[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
let attributeClasses = await prisma.attributeClass.findMany({
|
||||
const attributeClasses = await prisma.attributeClass.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "asc",
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
const transformedAttributeClasses: TAttributeClass[] = attributeClasses
|
||||
.map(transformPrismaAttributeClass)
|
||||
.filter((attributeClass): attributeClass is TAttributeClass => attributeClass !== null);
|
||||
|
||||
return transformedAttributeClasses;
|
||||
return attributeClasses;
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching attributeClasses for environment ${environmentId}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const updatetAttributeClass = async (
|
||||
attributeClassId: string,
|
||||
@@ -75,7 +66,7 @@ export const updatetAttributeClass = async (
|
||||
): Promise<TAttributeClass | null> => {
|
||||
validateInputs([attributeClassId, ZId], [data, ZAttributeClassUpdateInput.partial()]);
|
||||
try {
|
||||
let attributeClass = await prisma.attributeClass.update({
|
||||
const attributeClass = await prisma.attributeClass.update({
|
||||
where: {
|
||||
id: attributeClassId,
|
||||
},
|
||||
@@ -84,10 +75,10 @@ export const updatetAttributeClass = async (
|
||||
archived: data.archived,
|
||||
},
|
||||
});
|
||||
const transformedAttributeClass: TAttributeClass | null = transformPrismaAttributeClass(attributeClass);
|
||||
|
||||
revalidateTag(attributeClassesCacheTag(attributeClass.environmentId));
|
||||
return transformedAttributeClass;
|
||||
|
||||
return attributeClass;
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when updating attribute class with id ${attributeClassId}`);
|
||||
}
|
||||
@@ -95,7 +86,7 @@ export const updatetAttributeClass = async (
|
||||
|
||||
export const getAttributeClassByNameCached = async (environmentId: string, name: string) =>
|
||||
await unstable_cache(
|
||||
async () => {
|
||||
async (): Promise<TAttributeClass | null> => {
|
||||
return await getAttributeClassByName(environmentId, name);
|
||||
},
|
||||
[`environments-${environmentId}-attributeClass-${name}`],
|
||||
@@ -105,17 +96,18 @@ export const getAttributeClassByNameCached = async (environmentId: string, name:
|
||||
}
|
||||
)();
|
||||
|
||||
export const getAttributeClassByName = cache(
|
||||
async (environmentId: string, name: string): Promise<TAttributeClass | null> => {
|
||||
const attributeClass = await prisma.attributeClass.findFirst({
|
||||
where: {
|
||||
environmentId,
|
||||
name,
|
||||
},
|
||||
});
|
||||
return transformPrismaAttributeClass(attributeClass);
|
||||
}
|
||||
);
|
||||
export const getAttributeClassByName = async (
|
||||
environmentId: string,
|
||||
name: string
|
||||
): Promise<TAttributeClass | null> => {
|
||||
const attributeClass = await prisma.attributeClass.findFirst({
|
||||
where: {
|
||||
environmentId,
|
||||
name,
|
||||
},
|
||||
});
|
||||
return attributeClass;
|
||||
};
|
||||
|
||||
export const createAttributeClass = async (
|
||||
environmentId: string,
|
||||
@@ -134,7 +126,7 @@ export const createAttributeClass = async (
|
||||
},
|
||||
});
|
||||
revalidateTag(attributeClassesCacheTag(environmentId));
|
||||
return transformPrismaAttributeClass(attributeClass);
|
||||
return attributeClass;
|
||||
};
|
||||
|
||||
export const deleteAttributeClass = async (attributeClassId: string): Promise<TAttributeClass> => {
|
||||
|
||||
@@ -57,7 +57,7 @@ export const MAIL_FROM = env.MAIL_FROM;
|
||||
|
||||
export const NEXTAUTH_SECRET = env.NEXTAUTH_SECRET;
|
||||
export const NEXTAUTH_URL = env.NEXTAUTH_URL;
|
||||
export const PEOPLE_PER_PAGE = 50;
|
||||
export const ITEMS_PER_PAGE = 50;
|
||||
|
||||
// Storage constants
|
||||
export const UPLOADS_DIR = path.resolve("./uploads");
|
||||
|
||||
@@ -11,9 +11,10 @@ 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/service";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
import { ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
|
||||
const selectDisplay = {
|
||||
id: true,
|
||||
@@ -150,58 +151,61 @@ export const markDisplayResponded = async (displayId: string): Promise<TDisplay>
|
||||
}
|
||||
};
|
||||
|
||||
export const getDisplaysOfPerson = cache(
|
||||
async (personId: string): Promise<TDisplaysWithSurveyName[] | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const displaysPrisma = await prisma.display.findMany({
|
||||
where: {
|
||||
personId: personId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
surveyId: true,
|
||||
responseId: true,
|
||||
survey: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
export const getDisplaysOfPerson = async (
|
||||
personId: string,
|
||||
page?: number
|
||||
): Promise<TDisplaysWithSurveyName[] | null> => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
try {
|
||||
const displaysPrisma = await prisma.display.findMany({
|
||||
where: {
|
||||
personId: personId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
surveyId: true,
|
||||
responseId: true,
|
||||
survey: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
status: true,
|
||||
},
|
||||
});
|
||||
status: true,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
if (!displaysPrisma) {
|
||||
throw new ResourceNotFoundError("Display from PersonId", personId);
|
||||
}
|
||||
|
||||
let displays: TDisplaysWithSurveyName[] = [];
|
||||
|
||||
displaysPrisma.forEach((displayPrisma) => {
|
||||
const display: TDisplaysWithSurveyName = {
|
||||
id: displayPrisma.id,
|
||||
createdAt: displayPrisma.createdAt,
|
||||
updatedAt: displayPrisma.updatedAt,
|
||||
person: null,
|
||||
surveyId: displayPrisma.surveyId,
|
||||
surveyName: displayPrisma.survey.name,
|
||||
responseId: displayPrisma.responseId,
|
||||
};
|
||||
displays.push(display);
|
||||
});
|
||||
|
||||
return displays;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
if (!displaysPrisma) {
|
||||
throw new ResourceNotFoundError("Display from PersonId", personId);
|
||||
}
|
||||
|
||||
let displays: TDisplaysWithSurveyName[] = [];
|
||||
|
||||
displaysPrisma.forEach((displayPrisma) => {
|
||||
const display: TDisplaysWithSurveyName = {
|
||||
id: displayPrisma.id,
|
||||
createdAt: displayPrisma.createdAt,
|
||||
updatedAt: displayPrisma.updatedAt,
|
||||
person: null,
|
||||
surveyId: displayPrisma.surveyId,
|
||||
surveyName: displayPrisma.survey.name,
|
||||
responseId: displayPrisma.responseId,
|
||||
};
|
||||
displays.push(display);
|
||||
});
|
||||
|
||||
return displays;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const deleteDisplayByResponseId = async (responseId: string, surveyId: string): Promise<void> => {
|
||||
validateInputs([responseId, ZId]);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
|
||||
export const hasUserEnvironmentAccess = async (userId: string, environmentId: string) => {
|
||||
return await unstable_cache(
|
||||
async () => {
|
||||
async (): Promise<boolean> => {
|
||||
validateInputs([userId, ZId], [environmentId, ZId]);
|
||||
const environment = await prisma.environment.findUnique({
|
||||
where: {
|
||||
|
||||
@@ -25,7 +25,7 @@ export const getEnvironmentsCacheTag = (productId: string) => `products-${produc
|
||||
|
||||
export const getEnvironment = (environmentId: string) =>
|
||||
unstable_cache(
|
||||
async () => {
|
||||
async (): Promise<TEnvironment> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
let environmentPrisma;
|
||||
|
||||
@@ -62,7 +62,7 @@ export const getEnvironment = (environmentId: string) =>
|
||||
|
||||
export const getEnvironments = async (productId: string): Promise<TEnvironment[]> =>
|
||||
unstable_cache(
|
||||
async () => {
|
||||
async (): Promise<TEnvironment[]> => {
|
||||
validateInputs([productId, ZId]);
|
||||
let productPrisma;
|
||||
try {
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import "server-only";
|
||||
|
||||
import { z } from "zod";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { DatabaseError, UnknownError } from "@formbricks/types/v1/errors";
|
||||
import { cache } from "react";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import {
|
||||
ZGoogleCredential,
|
||||
TGoogleCredential,
|
||||
TGoogleSheetIntegration,
|
||||
TGoogleSpreadsheet,
|
||||
TIntegration,
|
||||
TGoogleSheetIntegration,
|
||||
} from "@formbricks/types/v1/integrations";
|
||||
import {
|
||||
GOOGLE_SHEETS_CLIENT_ID,
|
||||
GOOGLE_SHEETS_CLIENT_SECRET,
|
||||
GOOGLE_SHEETS_REDIRECT_URL,
|
||||
} from "../constants";
|
||||
import { ZString } from "@formbricks/types/v1/common";
|
||||
|
||||
const { google } = require("googleapis");
|
||||
|
||||
@@ -31,38 +36,36 @@ async function fetchSpreadsheets(auth: any) {
|
||||
}
|
||||
}
|
||||
|
||||
export const getGoogleSheetIntegration = cache(
|
||||
async (environmentId: string): Promise<TGoogleSheetIntegration | null> => {
|
||||
try {
|
||||
const result = await prisma.integration.findUnique({
|
||||
where: {
|
||||
type_environmentId: {
|
||||
environmentId,
|
||||
type: "googleSheets",
|
||||
},
|
||||
export const getGoogleSheetIntegration = async (
|
||||
environmentId: string
|
||||
): Promise<TIntegration | TGoogleSheetIntegration | null> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const result = await prisma.integration.findUnique({
|
||||
where: {
|
||||
type_environmentId: {
|
||||
environmentId,
|
||||
type: "googleSheets",
|
||||
},
|
||||
});
|
||||
// Type Guard
|
||||
if (result && isGoogleSheetIntegration(result)) {
|
||||
return result as TGoogleSheetIntegration; // Explicit casting
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
function isGoogleSheetIntegration(integration: any): integration is TGoogleSheetIntegration {
|
||||
return integration.type === "googleSheets";
|
||||
}
|
||||
};
|
||||
|
||||
export const getSpreadSheets = async (environmentId: string): Promise<TGoogleSpreadsheet[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
let spreadsheets: TGoogleSpreadsheet[] = [];
|
||||
try {
|
||||
const googleIntegration = await getGoogleSheetIntegration(environmentId);
|
||||
const googleIntegration = (await getGoogleSheetIntegration(environmentId)) as TGoogleSheetIntegration;
|
||||
if (googleIntegration && googleIntegration.config?.key) {
|
||||
spreadsheets = await fetchSpreadsheets(googleIntegration.config?.key);
|
||||
}
|
||||
@@ -75,6 +78,12 @@ export const getSpreadSheets = async (environmentId: string): Promise<TGoogleSpr
|
||||
}
|
||||
};
|
||||
export async function writeData(credentials: TGoogleCredential, spreadsheetId: string, values: string[][]) {
|
||||
validateInputs(
|
||||
[credentials, ZGoogleCredential],
|
||||
[spreadsheetId, ZString],
|
||||
[values, z.array(z.array(ZString))]
|
||||
);
|
||||
|
||||
try {
|
||||
const authClient = authorize(credentials);
|
||||
const sheets = google.sheets({ version: "v4", auth: authClient });
|
||||
|
||||
@@ -3,13 +3,18 @@ import "server-only";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { DatabaseError } from "@formbricks/types/v1/errors";
|
||||
import { TIntegration } from "@formbricks/types/v1/integrations";
|
||||
import { cache } from "react";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { TIntegration, TIntegrationInput } from "@formbricks/types/v1/integrations";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZString, ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
|
||||
export async function createOrUpdateIntegration(
|
||||
environmentId: string,
|
||||
integrationData: any
|
||||
integrationData: TIntegrationInput
|
||||
): Promise<TIntegration> {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const integration = await prisma.integration.upsert({
|
||||
where: {
|
||||
@@ -37,12 +42,16 @@ export async function createOrUpdateIntegration(
|
||||
}
|
||||
}
|
||||
|
||||
export const getIntegrations = cache(async (environmentId: string): Promise<TIntegration[]> => {
|
||||
export const getIntegrations = async (environmentId: string, page?: number): Promise<TIntegration[]> => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const result = await prisma.integration.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
@@ -51,9 +60,9 @@ export const getIntegrations = cache(async (environmentId: string): Promise<TInt
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getIntegration = cache(async (integrationId: string): Promise<TIntegration | null> => {
|
||||
export const getIntegration = async (integrationId: string): Promise<TIntegration | null> => {
|
||||
try {
|
||||
const result = await prisma.integration.findUnique({
|
||||
where: {
|
||||
@@ -67,15 +76,19 @@ export const getIntegration = cache(async (integrationId: string): Promise<TInte
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteIntegration = async (integrationId: string): Promise<TIntegration> => {
|
||||
validateInputs([integrationId, ZString]);
|
||||
|
||||
export const deleteIntegration = async (integrationId: string): Promise<void> => {
|
||||
try {
|
||||
await prisma.integration.delete({
|
||||
const integrationData = await prisma.integration.delete({
|
||||
where: {
|
||||
id: integrationId,
|
||||
},
|
||||
});
|
||||
|
||||
return integrationData;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
|
||||
@@ -2,11 +2,20 @@ import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { TInvite, TInviteUpdateInput } from "@formbricks/types/v1/invites";
|
||||
import { cache } from "react";
|
||||
import { ResourceNotFoundError, ValidationError } from "@formbricks/types/v1/errors";
|
||||
import {
|
||||
TInvite,
|
||||
TInvitee,
|
||||
ZInvitee,
|
||||
TInviteUpdateInput,
|
||||
ZInviteUpdateInput,
|
||||
ZCurrentUser,
|
||||
TCurrentUser,
|
||||
} from "@formbricks/types/v1/invites";
|
||||
import { ResourceNotFoundError, ValidationError, DatabaseError } from "@formbricks/types/v1/errors";
|
||||
import { ZString, ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
import { sendInviteMemberEmail } from "../emails/emails";
|
||||
import { TMembershipRole } from "@formbricks/types/v1/memberships";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
|
||||
const inviteSelect = {
|
||||
id: true,
|
||||
@@ -21,20 +30,22 @@ const inviteSelect = {
|
||||
role: true,
|
||||
};
|
||||
|
||||
export const getInvitesByTeamId = cache(async (teamId: string): Promise<TInvite[] | null> => {
|
||||
export const getInvitesByTeamId = async (teamId: string, page?: number): Promise<TInvite[] | null> => {
|
||||
validateInputs([teamId, ZString], [page, ZOptionalNumber]);
|
||||
|
||||
const invites = await prisma.invite.findMany({
|
||||
where: { teamId },
|
||||
select: inviteSelect,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
if (!invites) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return invites;
|
||||
});
|
||||
};
|
||||
|
||||
export const updateInvite = async (inviteId: string, data: TInviteUpdateInput): Promise<TInvite | null> => {
|
||||
validateInputs([inviteId, ZString], [data, ZInviteUpdateInput]);
|
||||
|
||||
export const updateInvite = async (inviteId: string, data: TInviteUpdateInput): Promise<TInvite> => {
|
||||
try {
|
||||
const invite = await prisma.invite.update({
|
||||
where: { id: inviteId },
|
||||
@@ -53,16 +64,32 @@ export const updateInvite = async (inviteId: string, data: TInviteUpdateInput):
|
||||
};
|
||||
|
||||
export const deleteInvite = async (inviteId: string): Promise<TInvite> => {
|
||||
const deletedInvite = await prisma.invite.delete({
|
||||
where: {
|
||||
id: inviteId,
|
||||
},
|
||||
});
|
||||
validateInputs([inviteId, ZString]);
|
||||
|
||||
return deletedInvite;
|
||||
try {
|
||||
const invite = await prisma.invite.delete({
|
||||
where: {
|
||||
id: inviteId,
|
||||
},
|
||||
});
|
||||
|
||||
if (invite === null) {
|
||||
throw new ResourceNotFoundError("Invite", inviteId);
|
||||
}
|
||||
|
||||
return invite;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const getInviteToken = cache(async (inviteId: string) => {
|
||||
export const getInvite = async (inviteId: string): Promise<{ inviteId: string; email: string }> => {
|
||||
validateInputs([inviteId, ZString]);
|
||||
|
||||
const invite = await prisma.invite.findUnique({
|
||||
where: {
|
||||
id: inviteId,
|
||||
@@ -80,9 +107,10 @@ export const getInviteToken = cache(async (inviteId: string) => {
|
||||
inviteId,
|
||||
email: invite.email,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const resendInvite = async (inviteId: string) => {
|
||||
export const resendInvite = async (inviteId: string): Promise<TInvite> => {
|
||||
validateInputs([inviteId, ZString]);
|
||||
const invite = await prisma.invite.findUnique({
|
||||
where: {
|
||||
id: inviteId,
|
||||
@@ -118,9 +146,11 @@ export const inviteUser = async ({
|
||||
teamId,
|
||||
}: {
|
||||
teamId: string;
|
||||
invitee: { name: string | null; email: string; role: TMembershipRole };
|
||||
currentUser: { id: string; name: string | null };
|
||||
}) => {
|
||||
invitee: TInvitee;
|
||||
currentUser: TCurrentUser;
|
||||
}): Promise<TInvite> => {
|
||||
validateInputs([teamId, ZString], [invitee, ZInvitee], [currentUser, ZCurrentUser]);
|
||||
|
||||
const { name, email, role } = invitee;
|
||||
const { id: currentUserId, name: currentUserName } = currentUser;
|
||||
const existingInvite = await prisma.invite.findFirst({ where: { email, teamId } });
|
||||
|
||||
@@ -2,13 +2,23 @@ import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ResourceNotFoundError, DatabaseError, UnknownError } from "@formbricks/types/v1/errors";
|
||||
import { TMember, TMembership, TMembershipUpdateInput } from "@formbricks/types/v1/memberships";
|
||||
import {
|
||||
TMember,
|
||||
TMembership,
|
||||
ZMembership,
|
||||
TMembershipUpdateInput,
|
||||
ZMembershipUpdateInput,
|
||||
} from "@formbricks/types/v1/memberships";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZString, ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
import { getTeamsByUserIdCacheTag } from "../team/service";
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
|
||||
export const getMembersByTeamId = async (teamId: string, page?: number): Promise<TMember[]> => {
|
||||
validateInputs([teamId, ZString], [page, ZOptionalNumber]);
|
||||
|
||||
export const getMembersByTeamId = cache(async (teamId: string): Promise<TMember[]> => {
|
||||
const membersData = await prisma.membership.findMany({
|
||||
where: { teamId },
|
||||
select: {
|
||||
@@ -22,6 +32,8 @@ export const getMembersByTeamId = cache(async (teamId: string): Promise<TMember[
|
||||
accepted: true,
|
||||
role: true,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
const members = membersData.map((member) => {
|
||||
@@ -35,26 +47,30 @@ export const getMembersByTeamId = cache(async (teamId: string): Promise<TMember[
|
||||
});
|
||||
|
||||
return members;
|
||||
});
|
||||
};
|
||||
|
||||
export const getMembershipByUserIdTeamId = cache(
|
||||
async (userId: string, teamId: string): Promise<TMembership | null> => {
|
||||
const membership = await prisma.membership.findUnique({
|
||||
where: {
|
||||
userId_teamId: {
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
export const getMembershipByUserIdTeamId = async (
|
||||
userId: string,
|
||||
teamId: string
|
||||
): Promise<TMembership | null> => {
|
||||
validateInputs([userId, ZString], [teamId, ZString]);
|
||||
|
||||
const membership = await prisma.membership.findUnique({
|
||||
where: {
|
||||
userId_teamId: {
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (!membership) return null;
|
||||
if (!membership) return null;
|
||||
|
||||
return membership;
|
||||
}
|
||||
);
|
||||
return membership;
|
||||
};
|
||||
|
||||
export const getMembershipsByUserId = cache(async (userId: string): Promise<TMembership[]> => {
|
||||
export const getMembershipsByUserId = async (userId: string): Promise<TMembership[]> => {
|
||||
validateInputs([userId, ZString]);
|
||||
const memberships = await prisma.membership.findMany({
|
||||
where: {
|
||||
userId,
|
||||
@@ -62,13 +78,14 @@ export const getMembershipsByUserId = cache(async (userId: string): Promise<TMem
|
||||
});
|
||||
|
||||
return memberships;
|
||||
});
|
||||
};
|
||||
|
||||
export const createMembership = async (
|
||||
teamId: string,
|
||||
userId: string,
|
||||
data: Partial<TMembership>
|
||||
): Promise<TMembership> => {
|
||||
validateInputs([teamId, ZString], [userId, ZString], [data, ZMembership.partial()]);
|
||||
try {
|
||||
const membership = await prisma.membership.create({
|
||||
data: {
|
||||
@@ -90,6 +107,8 @@ export const updateMembership = async (
|
||||
teamId: string,
|
||||
data: TMembershipUpdateInput
|
||||
): Promise<TMembership> => {
|
||||
validateInputs([userId, ZString], [teamId, ZString], [data, ZMembershipUpdateInput]);
|
||||
|
||||
try {
|
||||
const membership = await prisma.membership.update({
|
||||
where: {
|
||||
@@ -112,6 +131,8 @@ export const updateMembership = async (
|
||||
};
|
||||
|
||||
export const deleteMembership = async (userId: string, teamId: string): Promise<TMembership> => {
|
||||
validateInputs([userId, ZString], [teamId, ZString]);
|
||||
|
||||
const deletedMembership = await prisma.membership.delete({
|
||||
where: {
|
||||
userId_teamId: {
|
||||
@@ -124,9 +145,15 @@ export const deleteMembership = async (userId: string, teamId: string): Promise<
|
||||
return deletedMembership;
|
||||
};
|
||||
|
||||
export const transferOwnership = async (currentOwnerId: string, newOwnerId: string, teamId: string) => {
|
||||
export const transferOwnership = async (
|
||||
currentOwnerId: string,
|
||||
newOwnerId: string,
|
||||
teamId: string
|
||||
): Promise<TMembership[]> => {
|
||||
validateInputs([currentOwnerId, ZString], [newOwnerId, ZString], [teamId, ZString]);
|
||||
|
||||
try {
|
||||
await prisma.$transaction([
|
||||
const memberships = await prisma.$transaction([
|
||||
prisma.membership.update({
|
||||
where: {
|
||||
userId_teamId: {
|
||||
@@ -150,6 +177,8 @@ export const transferOwnership = async (currentOwnerId: string, newOwnerId: stri
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return memberships;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
|
||||
@@ -3,14 +3,13 @@ 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, TPersonUpdateInput } from "@formbricks/types/v1/people";
|
||||
import { TPerson, TPersonUpdateInput, ZPersonUpdateInput } from "@formbricks/types/v1/people";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { revalidateTag, unstable_cache } from "next/cache";
|
||||
import { cache } from "react";
|
||||
import { PEOPLE_PER_PAGE } from "../constants";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { getAttributeClassByName } from "../attributeClass/service";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { SERVICES_REVALIDATION_INTERVAL, ITEMS_PER_PAGE } from "../constants";
|
||||
import { ZString, ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
|
||||
export const selectPerson = {
|
||||
id: true,
|
||||
@@ -62,26 +61,25 @@ export const transformPrismaPerson = (person: TransformPersonInput): TPerson =>
|
||||
environmentId: person.environmentId,
|
||||
createdAt: person.createdAt,
|
||||
updatedAt: person.updatedAt,
|
||||
};
|
||||
} as TPerson;
|
||||
};
|
||||
|
||||
export const getPerson = cache(async (personId: string): Promise<TPerson | null> => {
|
||||
export const getPerson = async (personId: string): Promise<TPerson | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
|
||||
try {
|
||||
const personPrisma = await prisma.person.findUnique({
|
||||
const person = await prisma.person.findUnique({
|
||||
where: {
|
||||
id: personId,
|
||||
},
|
||||
select: selectPerson,
|
||||
});
|
||||
|
||||
if (!personPrisma) {
|
||||
if (!person) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const person = transformPrismaPerson(personPrisma);
|
||||
|
||||
return person;
|
||||
return transformPrismaPerson(person);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
@@ -89,13 +87,15 @@ export const getPerson = cache(async (personId: string): Promise<TPerson | null>
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getPersonCacheKey = (personId: string): string[] => [personId];
|
||||
|
||||
export const getPersonCached = async (personId: string) =>
|
||||
await unstable_cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId]);
|
||||
|
||||
return await getPerson(personId);
|
||||
},
|
||||
getPersonCacheKey(personId),
|
||||
@@ -105,28 +105,26 @@ export const getPersonCached = async (personId: string) =>
|
||||
}
|
||||
)();
|
||||
|
||||
export const getPeople = cache(async (environmentId: string, page: number = 1): Promise<TPerson[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getPeople = async (environmentId: string, page?: number): Promise<TPerson[]> => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const itemsPerPage = PEOPLE_PER_PAGE;
|
||||
const people = await prisma.person.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
select: selectPerson,
|
||||
take: itemsPerPage,
|
||||
skip: itemsPerPage * (page - 1),
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
if (!people || people.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const transformedPeople: TPerson[] = people
|
||||
return people
|
||||
.map(transformPrismaPerson)
|
||||
.filter((person: TPerson | null): person is TPerson => person !== null);
|
||||
|
||||
return transformedPeople;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
@@ -134,17 +132,17 @@ export const getPeople = cache(async (environmentId: string, page: number = 1):
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getPeopleCount = cache(async (environmentId: string): Promise<number> => {
|
||||
export const getPeopleCount = async (environmentId: string): Promise<number> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const totalCount = await prisma.person.count({
|
||||
return await prisma.person.count({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
});
|
||||
return totalCount;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
@@ -152,12 +150,13 @@ export const getPeopleCount = cache(async (environmentId: string): Promise<numbe
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const createPerson = async (environmentId: string): Promise<TPerson> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const personPrisma = await prisma.person.create({
|
||||
const person = await prisma.person.create({
|
||||
data: {
|
||||
environment: {
|
||||
connect: {
|
||||
@@ -168,14 +167,14 @@ export const createPerson = async (environmentId: string): Promise<TPerson> => {
|
||||
select: selectPerson,
|
||||
});
|
||||
|
||||
const person = transformPrismaPerson(personPrisma);
|
||||
const transformedPerson = transformPrismaPerson(person);
|
||||
|
||||
if (person) {
|
||||
if (transformedPerson) {
|
||||
// revalidate person
|
||||
revalidateTag(person.id);
|
||||
revalidateTag(transformedPerson.id);
|
||||
}
|
||||
|
||||
return person;
|
||||
return transformedPerson;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
@@ -185,17 +184,24 @@ export const createPerson = async (environmentId: string): Promise<TPerson> => {
|
||||
}
|
||||
};
|
||||
|
||||
export const deletePerson = async (personId: string): Promise<void> => {
|
||||
export const deletePerson = async (personId: string): Promise<TPerson | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
|
||||
try {
|
||||
await prisma.person.delete({
|
||||
const person = await prisma.person.delete({
|
||||
where: {
|
||||
id: personId,
|
||||
},
|
||||
select: selectPerson,
|
||||
});
|
||||
const transformedPerson = transformPrismaPerson(person);
|
||||
|
||||
// revalidate person
|
||||
revalidateTag(personId);
|
||||
if (transformedPerson) {
|
||||
// revalidate person
|
||||
revalidateTag(personId);
|
||||
}
|
||||
|
||||
return transformedPerson;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
@@ -206,8 +212,10 @@ export const deletePerson = async (personId: string): Promise<void> => {
|
||||
};
|
||||
|
||||
export const updatePerson = async (personId: string, personInput: TPersonUpdateInput): Promise<TPerson> => {
|
||||
validateInputs([personId, ZId], [personInput, ZPersonUpdateInput]);
|
||||
|
||||
try {
|
||||
const personPrisma = await prisma.person.update({
|
||||
const person = await prisma.person.update({
|
||||
where: {
|
||||
id: personId,
|
||||
},
|
||||
@@ -215,8 +223,7 @@ export const updatePerson = async (personId: string, personInput: TPersonUpdateI
|
||||
select: selectPerson,
|
||||
});
|
||||
|
||||
const person = transformPrismaPerson(personPrisma);
|
||||
return person;
|
||||
return transformPrismaPerson(person);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
@@ -226,8 +233,10 @@ export const updatePerson = async (personId: string, personInput: TPersonUpdateI
|
||||
}
|
||||
};
|
||||
export const getOrCreatePersonByUserId = async (userId: string, environmentId: string): Promise<TPerson> => {
|
||||
validateInputs([userId, ZString], [environmentId, ZId]);
|
||||
|
||||
// Check if a person with the userId attribute exists
|
||||
const personPrisma = await prisma.person.findFirst({
|
||||
const person = await prisma.person.findFirst({
|
||||
where: {
|
||||
environmentId,
|
||||
attributes: {
|
||||
@@ -242,9 +251,8 @@ export const getOrCreatePersonByUserId = async (userId: string, environmentId: s
|
||||
select: selectPerson,
|
||||
});
|
||||
|
||||
if (personPrisma) {
|
||||
const person = transformPrismaPerson(personPrisma);
|
||||
return person;
|
||||
if (person) {
|
||||
return transformPrismaPerson(person);
|
||||
} else {
|
||||
// Create a new person with the userId attribute
|
||||
const userIdAttributeClass = await getAttributeClassByName(environmentId, "userId");
|
||||
@@ -253,7 +261,7 @@ export const getOrCreatePersonByUserId = async (userId: string, environmentId: s
|
||||
throw new ResourceNotFoundError("Attribute class not found for the given environment", environmentId);
|
||||
}
|
||||
|
||||
const personPrisma = await prisma.person.create({
|
||||
const person = await prisma.person.create({
|
||||
data: {
|
||||
environment: {
|
||||
connect: {
|
||||
@@ -276,22 +284,24 @@ export const getOrCreatePersonByUserId = async (userId: string, environmentId: s
|
||||
select: selectPerson,
|
||||
});
|
||||
|
||||
if (personPrisma) {
|
||||
if (person) {
|
||||
// revalidate person
|
||||
revalidateTag(personPrisma.id);
|
||||
revalidateTag(person.id);
|
||||
}
|
||||
|
||||
return transformPrismaPerson(personPrisma);
|
||||
return transformPrismaPerson(person);
|
||||
}
|
||||
};
|
||||
|
||||
export const getMonthlyActivePeopleCount = async (environmentId: string): Promise<number> =>
|
||||
await unstable_cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
const now = new Date();
|
||||
const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
|
||||
const aggregations = await prisma.person.aggregate({
|
||||
const personAggregations = await prisma.person.aggregate({
|
||||
_count: {
|
||||
id: true,
|
||||
},
|
||||
@@ -307,7 +317,7 @@ export const getMonthlyActivePeopleCount = async (environmentId: string): Promis
|
||||
},
|
||||
});
|
||||
|
||||
return aggregations._count.id;
|
||||
return personAggregations._count.id;
|
||||
},
|
||||
[`environments-${environmentId}-mau`],
|
||||
{
|
||||
@@ -320,8 +330,9 @@ export const updatePersonAttribute = async (
|
||||
personId: string,
|
||||
attributeClassId: string,
|
||||
value: string
|
||||
): Promise<void> => {
|
||||
await prisma.attribute.upsert({
|
||||
): Promise<Partial<TPerson>> => {
|
||||
validateInputs([personId, ZId], [attributeClassId, ZId], [value, ZString]);
|
||||
const attributes = await prisma.attribute.upsert({
|
||||
where: {
|
||||
attributeClassId_personId: {
|
||||
attributeClassId,
|
||||
@@ -348,4 +359,6 @@ export const updatePersonAttribute = async (
|
||||
|
||||
// revalidate person
|
||||
revalidateTag(personId);
|
||||
|
||||
return attributes;
|
||||
};
|
||||
|
||||
@@ -7,11 +7,11 @@ 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 { z } from "zod";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { SERVICES_REVALIDATION_INTERVAL, ITEMS_PER_PAGE } from "../constants";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { createEnvironment, getEnvironmentCacheTag, getEnvironmentsCacheTag } from "../environment/service";
|
||||
import { ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
|
||||
export const getProductsCacheTag = (teamId: string): string => `teams-${teamId}-products`;
|
||||
export const getProductCacheTag = (environmentId: string): string => `environments-${environmentId}-product`;
|
||||
@@ -33,16 +33,19 @@ const selectProduct = {
|
||||
environments: true,
|
||||
};
|
||||
|
||||
export const getProducts = async (teamId: string): Promise<TProduct[]> =>
|
||||
export const getProducts = async (teamId: string, page?: number): Promise<TProduct[]> =>
|
||||
unstable_cache(
|
||||
async () => {
|
||||
validateInputs([teamId, ZId]);
|
||||
validateInputs([teamId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const products = await prisma.product.findMany({
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
select: selectProduct,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
return products;
|
||||
@@ -61,7 +64,7 @@ export const getProducts = async (teamId: string): Promise<TProduct[]> =>
|
||||
}
|
||||
)();
|
||||
|
||||
export const getProductByEnvironmentId = cache(async (environmentId: string): Promise<TProduct | null> => {
|
||||
export const getProductByEnvironmentId = async (environmentId: string): Promise<TProduct | null> => {
|
||||
if (!environmentId) {
|
||||
throw new ValidationError("EnvironmentId is required");
|
||||
}
|
||||
@@ -86,9 +89,9 @@ export const getProductByEnvironmentId = cache(async (environmentId: string): Pr
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getProductByEnvironmentIdCached = (environmentId: string) =>
|
||||
export const getProductByEnvironmentIdCached = (environmentId: string): Promise<TProduct | null> =>
|
||||
unstable_cache(
|
||||
async () => {
|
||||
return await getProductByEnvironmentId(environmentId);
|
||||
@@ -144,7 +147,7 @@ export const updateProduct = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getProduct = cache(async (productId: string): Promise<TProduct | null> => {
|
||||
export const getProduct = async (productId: string): Promise<TProduct | null> => {
|
||||
let productPrisma;
|
||||
try {
|
||||
productPrisma = await prisma.product.findUnique({
|
||||
@@ -161,9 +164,9 @@ export const getProduct = cache(async (productId: string): Promise<TProduct | nu
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteProduct = cache(async (productId: string): Promise<TProduct> => {
|
||||
export const deleteProduct = async (productId: string): Promise<TProduct> => {
|
||||
const product = await prisma.product.delete({
|
||||
where: {
|
||||
id: productId,
|
||||
@@ -182,7 +185,7 @@ export const deleteProduct = cache(async (productId: string): Promise<TProduct>
|
||||
}
|
||||
|
||||
return product;
|
||||
});
|
||||
};
|
||||
|
||||
export const createProduct = async (
|
||||
teamId: string,
|
||||
|
||||
@@ -170,7 +170,7 @@ export const createProfile = async (data: TProfileCreateInput): Promise<TProfile
|
||||
};
|
||||
|
||||
// function to delete a user's profile including teams
|
||||
export const deleteProfile = async (userId: string): Promise<void> => {
|
||||
export const deleteProfile = async (userId: string): Promise<TProfile> => {
|
||||
validateInputs([userId, ZId]);
|
||||
try {
|
||||
const currentUserMemberships = await prisma.membership.findMany({
|
||||
@@ -209,7 +209,10 @@ export const deleteProfile = async (userId: string): Promise<void> => {
|
||||
}
|
||||
|
||||
revalidateTag(getProfileCacheTag(userId));
|
||||
await deleteUser(userId);
|
||||
|
||||
const deletedProfile = await deleteUser(userId);
|
||||
|
||||
return deletedProfile;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
|
||||
@@ -11,15 +11,15 @@ import {
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { TPerson } from "@formbricks/types/v1/people";
|
||||
import { TTag } from "@formbricks/types/v1/tags";
|
||||
import { z } from "zod";
|
||||
import { cache } from "react";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { getPerson, transformPrismaPerson } from "../person/service";
|
||||
import { captureTelemetry } from "../telemetry";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { deleteDisplayByResponseId } from "../display/service";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { ZString, ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
|
||||
const responseSelection = {
|
||||
id: true,
|
||||
@@ -84,14 +84,20 @@ export const getResponsesCacheTag = (surveyId: string) => `surveys-${surveyId}-r
|
||||
|
||||
export const getResponseCacheTag = (responseId: string) => `responses-${responseId}`;
|
||||
|
||||
export const getResponsesByPersonId = async (personId: string): Promise<Array<TResponse> | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
export const getResponsesByPersonId = async (
|
||||
personId: string,
|
||||
page?: number
|
||||
): Promise<Array<TResponse> | null> => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findMany({
|
||||
where: {
|
||||
personId,
|
||||
},
|
||||
select: responseSelection,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
if (!responsePrisma) {
|
||||
@@ -118,44 +124,47 @@ export const getResponsesByPersonId = async (personId: string): Promise<Array<TR
|
||||
}
|
||||
};
|
||||
|
||||
export const getResponseBySingleUseId = cache(
|
||||
async (surveyId: string, singleUseId?: string): Promise<TResponse | null> => {
|
||||
validateInputs([surveyId, ZId], [singleUseId, z.string()]);
|
||||
try {
|
||||
if (!singleUseId) {
|
||||
return null;
|
||||
}
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
surveyId_singleUseId: { surveyId, singleUseId },
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
export const getResponseBySingleUseId = async (
|
||||
surveyId: string,
|
||||
singleUseId?: string
|
||||
): Promise<TResponse | null> => {
|
||||
validateInputs([surveyId, ZId], [singleUseId, ZString]);
|
||||
|
||||
if (!responsePrisma) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const response: TResponse = {
|
||||
...responsePrisma,
|
||||
person: responsePrisma.person ? transformPrismaPerson(responsePrisma.person) : null,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
try {
|
||||
if (!singleUseId) {
|
||||
return null;
|
||||
}
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
surveyId_singleUseId: { surveyId, singleUseId },
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
|
||||
if (!responsePrisma) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const response: TResponse = {
|
||||
...responsePrisma,
|
||||
person: responsePrisma.person ? transformPrismaPerson(responsePrisma.person) : null,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const createResponse = async (responseInput: Partial<TResponseInput>): Promise<TResponse> => {
|
||||
validateInputs([responseInput, ZResponseInput.partial()]);
|
||||
captureTelemetry("response created");
|
||||
|
||||
try {
|
||||
let person: TPerson | null = null;
|
||||
|
||||
@@ -208,6 +217,7 @@ export const createResponse = async (responseInput: Partial<TResponseInput>): Pr
|
||||
|
||||
export const getResponse = async (responseId: string): Promise<TResponse | null> => {
|
||||
validateInputs([responseId, ZId]);
|
||||
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
@@ -236,15 +246,11 @@ export const getResponse = async (responseId: string): Promise<TResponse | null>
|
||||
}
|
||||
};
|
||||
|
||||
export const preloadSurveyResponses = (surveyId: string) => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
void getSurveyResponses(surveyId);
|
||||
};
|
||||
export const getSurveyResponses = async (surveyId: string, page?: number): Promise<TResponse[]> => {
|
||||
validateInputs([surveyId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
export const getSurveyResponses = cache(async (surveyId: string): Promise<TResponse[]> => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
try {
|
||||
const responsesPrisma = await prisma.response.findMany({
|
||||
const responses = await prisma.response.findMany({
|
||||
where: {
|
||||
surveyId,
|
||||
},
|
||||
@@ -254,15 +260,17 @@ export const getSurveyResponses = cache(async (surveyId: string): Promise<TRespo
|
||||
createdAt: "desc",
|
||||
},
|
||||
],
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
const responses: TResponse[] = responsesPrisma.map((responsePrisma) => ({
|
||||
const transformedResponses: TResponse[] = responses.map((responsePrisma) => ({
|
||||
...responsePrisma,
|
||||
person: responsePrisma.person ? transformPrismaPerson(responsePrisma.person) : null,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
}));
|
||||
|
||||
return responses;
|
||||
return transformedResponses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
@@ -270,17 +278,13 @@ export const getSurveyResponses = cache(async (surveyId: string): Promise<TRespo
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
export const preloadEnvironmentResponses = (environmentId: string) => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
void getEnvironmentResponses(environmentId);
|
||||
};
|
||||
|
||||
export const getEnvironmentResponses = cache(async (environmentId: string): Promise<TResponse[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getEnvironmentResponses = async (environmentId: string, page?: number): Promise<TResponse[]> => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const responsesPrisma = await prisma.response.findMany({
|
||||
const responses = await prisma.response.findMany({
|
||||
where: {
|
||||
survey: {
|
||||
environmentId,
|
||||
@@ -292,15 +296,17 @@ export const getEnvironmentResponses = cache(async (environmentId: string): Prom
|
||||
createdAt: "desc",
|
||||
},
|
||||
],
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
const responses: TResponse[] = responsesPrisma.map((responsePrisma) => ({
|
||||
const transformedResponses: TResponse[] = responses.map((responsePrisma) => ({
|
||||
...responsePrisma,
|
||||
person: responsePrisma.person ? transformPrismaPerson(responsePrisma.person) : null,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
}));
|
||||
|
||||
return responses;
|
||||
return transformedResponses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
@@ -308,7 +314,7 @@ export const getEnvironmentResponses = cache(async (environmentId: string): Prom
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const updateResponse = async (
|
||||
responseId: string,
|
||||
|
||||
@@ -7,10 +7,9 @@ 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";
|
||||
|
||||
const halfHourInSeconds = 60 * 30;
|
||||
import { ZOptionalNumber } from "@formbricks/types/v1/common";
|
||||
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
|
||||
const getSessionCacheKey = (sessionId: string): string[] => [sessionId];
|
||||
|
||||
@@ -52,14 +51,15 @@ export const getSessionCached = (sessionId: string) =>
|
||||
getSessionCacheKey(sessionId),
|
||||
{
|
||||
tags: getSessionCacheKey(sessionId),
|
||||
revalidate: halfHourInSeconds, // 30 minutes
|
||||
revalidate: SERVICES_REVALIDATION_INTERVAL,
|
||||
}
|
||||
)();
|
||||
|
||||
export const getSessionWithActionsOfPerson = async (
|
||||
personId: string
|
||||
personId: string,
|
||||
page?: number
|
||||
): Promise<TSessionWithActions[] | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
try {
|
||||
const sessionsWithActionsForPerson = await prisma.session.findMany({
|
||||
where: {
|
||||
@@ -81,6 +81,8 @@ export const getSessionWithActionsOfPerson = async (
|
||||
},
|
||||
},
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
if (!sessionsWithActionsForPerson) return null;
|
||||
|
||||
@@ -93,7 +95,7 @@ export const getSessionWithActionsOfPerson = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getSessionCount = cache(async (personId: string): Promise<number> => {
|
||||
export const getSessionCount = async (personId: string): Promise<number> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const sessionCount = await prisma.session.count({
|
||||
@@ -108,7 +110,7 @@ export const getSessionCount = cache(async (personId: string): Promise<number> =
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const createSession = async (personId: string): Promise<TSession> => {
|
||||
validateInputs([personId, ZId]);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
TSurveyWithAnalytics,
|
||||
ZSurvey,
|
||||
ZSurveyWithAnalytics,
|
||||
TSurveyInput,
|
||||
} from "@formbricks/types/v1/surveys";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { revalidateTag, unstable_cache } from "next/cache";
|
||||
@@ -17,6 +18,9 @@ import { captureTelemetry } from "../telemetry";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { getDisplaysCacheTag } from "../display/service";
|
||||
import { getResponsesCacheTag } from "../response/service";
|
||||
import { ZString } from "@formbricks/types/v1/common";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { getActionClasses } from "../actionClass/service";
|
||||
|
||||
// surveys cache key and tags
|
||||
const getSurveysCacheTag = (environmentId: string): string => `environments-${environmentId}-surveys`;
|
||||
@@ -88,6 +92,8 @@ export const selectSurveyWithAnalytics = {
|
||||
};
|
||||
|
||||
export const getSurveyWithAnalytics = async (surveyId: string): Promise<TSurveyWithAnalytics | null> => {
|
||||
validateInputs([surveyId, ZString]);
|
||||
|
||||
const survey = await unstable_cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
@@ -126,7 +132,7 @@ export const getSurveyWithAnalytics = async (surveyId: string): Promise<TSurveyW
|
||||
|
||||
const transformedSurvey = {
|
||||
...surveyPrismaFields,
|
||||
triggers: surveyPrismaFields.triggers.map((trigger) => trigger.eventClass),
|
||||
triggers: surveyPrismaFields.triggers.map((trigger) => trigger.eventClass.name),
|
||||
analytics: {
|
||||
numDisplays,
|
||||
responseRate,
|
||||
@@ -150,7 +156,7 @@ export const getSurveyWithAnalytics = async (surveyId: string): Promise<TSurveyW
|
||||
[`surveyWithAnalytics-${surveyId}`],
|
||||
{
|
||||
tags: [getSurveyCacheTag(surveyId), getDisplaysCacheTag(surveyId), getResponsesCacheTag(surveyId)],
|
||||
revalidate: 60 * 30,
|
||||
revalidate: SERVICES_REVALIDATION_INTERVAL,
|
||||
}
|
||||
)();
|
||||
|
||||
@@ -196,7 +202,7 @@ export const getSurvey = async (surveyId: string): Promise<TSurvey | null> => {
|
||||
|
||||
const transformedSurvey = {
|
||||
...surveyPrisma,
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass),
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass.name),
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -215,7 +221,7 @@ export const getSurvey = async (surveyId: string): Promise<TSurvey | null> => {
|
||||
[`surveys-${surveyId}`],
|
||||
{
|
||||
tags: [getSurveyCacheTag(surveyId)],
|
||||
revalidate: 60 * 30,
|
||||
revalidate: SERVICES_REVALIDATION_INTERVAL,
|
||||
}
|
||||
)();
|
||||
|
||||
@@ -250,7 +256,7 @@ export const getSurveysByAttributeClassId = async (attributeClassId: string): Pr
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = {
|
||||
...surveyPrisma,
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass),
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass.name),
|
||||
};
|
||||
const survey = ZSurvey.parse(transformedSurvey);
|
||||
surveys.push(survey);
|
||||
@@ -287,7 +293,7 @@ export const getSurveysByActionClassId = async (actionClassId: string): Promise<
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = {
|
||||
...surveyPrisma,
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass),
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass.name),
|
||||
};
|
||||
const survey = ZSurvey.parse(transformedSurvey);
|
||||
surveys.push(survey);
|
||||
@@ -333,7 +339,7 @@ export const getSurveys = async (environmentId: string): Promise<TSurvey[]> => {
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = {
|
||||
...surveyPrisma,
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass),
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass.name),
|
||||
};
|
||||
const survey = ZSurvey.parse(transformedSurvey);
|
||||
surveys.push(survey);
|
||||
@@ -352,7 +358,7 @@ export const getSurveys = async (environmentId: string): Promise<TSurvey[]> => {
|
||||
[`environments-${environmentId}-surveys`],
|
||||
{
|
||||
tags: [getSurveysCacheTag(environmentId)],
|
||||
revalidate: 60 * 30,
|
||||
revalidate: SERVICES_REVALIDATION_INTERVAL,
|
||||
}
|
||||
)();
|
||||
|
||||
@@ -400,7 +406,7 @@ export const getSurveysWithAnalytics = async (environmentId: string): Promise<TS
|
||||
|
||||
const transformedSurvey = {
|
||||
...surveyPrisma,
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass),
|
||||
triggers: surveyPrisma.triggers.map((trigger) => trigger.eventClass.name),
|
||||
analytics: {
|
||||
numDisplays,
|
||||
responseRate,
|
||||
@@ -436,15 +442,17 @@ export const getSurveysWithAnalytics = async (environmentId: string): Promise<TS
|
||||
}));
|
||||
};
|
||||
|
||||
export async function updateSurvey(updatedSurvey: Partial<TSurvey>): Promise<TSurvey> {
|
||||
export async function updateSurvey(updatedSurvey: TSurvey): Promise<TSurvey> {
|
||||
validateInputs([updatedSurvey, ZSurvey]);
|
||||
|
||||
const surveyId = updatedSurvey.id;
|
||||
let data: any = {};
|
||||
let survey: any = { ...updatedSurvey };
|
||||
|
||||
if (updatedSurvey.triggers && updatedSurvey.triggers.length > 0) {
|
||||
const modifiedTriggers = updatedSurvey.triggers.map((trigger) => {
|
||||
if (typeof trigger === "object" && trigger.id) {
|
||||
return trigger.id;
|
||||
if (typeof trigger === "object" && trigger) {
|
||||
return trigger;
|
||||
} else if (typeof trigger === "string" && trigger !== undefined) {
|
||||
return trigger;
|
||||
}
|
||||
@@ -453,10 +461,15 @@ export async function updateSurvey(updatedSurvey: Partial<TSurvey>): Promise<TSu
|
||||
survey = { ...updatedSurvey, triggers: modifiedTriggers };
|
||||
}
|
||||
|
||||
const actionClasses = await getActionClasses(updatedSurvey.environmentId);
|
||||
|
||||
const currentTriggers = await prisma.surveyTrigger.findMany({
|
||||
where: {
|
||||
surveyId,
|
||||
},
|
||||
include: {
|
||||
eventClass: true,
|
||||
},
|
||||
});
|
||||
const currentAttributeFilters = await prisma.surveyAttributeFilter.findMany({
|
||||
where: {
|
||||
@@ -481,30 +494,30 @@ export async function updateSurvey(updatedSurvey: Partial<TSurvey>): Promise<TSu
|
||||
const newTriggers: string[] = [];
|
||||
const removedTriggers: string[] = [];
|
||||
// find added triggers
|
||||
for (const eventClassId of survey.triggers) {
|
||||
if (!eventClassId) {
|
||||
for (const eventClassName of survey.triggers) {
|
||||
if (!eventClassName) {
|
||||
continue;
|
||||
}
|
||||
if (currentTriggers.find((t) => t.eventClassId === eventClassId)) {
|
||||
if (currentTriggers.find((t) => t.eventClass.name === eventClassName)) {
|
||||
continue;
|
||||
} else {
|
||||
newTriggers.push(eventClassId);
|
||||
newTriggers.push(eventClassName);
|
||||
}
|
||||
}
|
||||
// find removed triggers
|
||||
for (const trigger of currentTriggers) {
|
||||
if (survey.triggers.find((t: any) => t === trigger.eventClassId)) {
|
||||
if (survey.triggers.find((t: any) => t === trigger.eventClass.name)) {
|
||||
continue;
|
||||
} else {
|
||||
removedTriggers.push(trigger.eventClassId);
|
||||
removedTriggers.push(trigger.eventClass.name);
|
||||
}
|
||||
}
|
||||
// create new triggers
|
||||
if (newTriggers.length > 0) {
|
||||
data.triggers = {
|
||||
...(data.triggers || []),
|
||||
create: newTriggers.map((eventClassId) => ({
|
||||
eventClassId,
|
||||
create: newTriggers.map((eventClassName) => ({
|
||||
eventClassId: actionClasses.find((actionClass) => actionClass.name === eventClassName)!.id,
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -638,22 +651,37 @@ export async function deleteSurvey(surveyId: string) {
|
||||
return deletedSurvey;
|
||||
}
|
||||
|
||||
export async function createSurvey(environmentId: string, surveyBody: any) {
|
||||
export async function createSurvey(environmentId: string, surveyBody: TSurveyInput): Promise<TSurvey> {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
// TODO: Create with triggers & attributeFilters
|
||||
delete surveyBody.triggers;
|
||||
delete surveyBody.attributeFilters;
|
||||
const data: Omit<TSurveyInput, "triggers" | "attributeFilters"> = {
|
||||
...surveyBody,
|
||||
};
|
||||
|
||||
const survey = await prisma.survey.create({
|
||||
data: {
|
||||
...surveyBody,
|
||||
...data,
|
||||
environment: {
|
||||
connect: {
|
||||
id: environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: selectSurvey,
|
||||
});
|
||||
|
||||
const transformedSurvey = {
|
||||
...survey,
|
||||
triggers: survey.triggers.map((trigger) => trigger.eventClass.name),
|
||||
};
|
||||
|
||||
captureTelemetry("survey created");
|
||||
|
||||
revalidateTag(getSurveysCacheTag(environmentId));
|
||||
revalidateTag(getSurveyCacheTag(survey.id));
|
||||
|
||||
return survey;
|
||||
return transformedSurvey;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TTag } from "@formbricks/types/v1/tags";
|
||||
import { cache } from "react";
|
||||
|
||||
export const getTagsByEnvironmentId = cache(async (environmentId: string): Promise<TTag[]> => {
|
||||
export const getTagsByEnvironmentId = async (environmentId: string): Promise<TTag[]> => {
|
||||
try {
|
||||
const tags = await prisma.tag.findMany({
|
||||
where: {
|
||||
@@ -16,7 +15,7 @@ export const getTagsByEnvironmentId = cache(async (environmentId: string): Promi
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getTag = async (tagId: string): Promise<TTag | null> => {
|
||||
try {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TTagsCount } from "@formbricks/types/v1/tags";
|
||||
import { cache } from "react";
|
||||
import { TTagsCount, TTagsOnResponses } from "@formbricks/types/v1/tags";
|
||||
|
||||
export const getTagOnResponseCacheTag = (tagId: string, responseId: string) =>
|
||||
`tagsOnResponse-${tagId}-${responseId}`;
|
||||
|
||||
export const addTagToRespone = async (responseId: string, tagId: string) => {
|
||||
export const addTagToRespone = async (responseId: string, tagId: string): Promise<TTagsOnResponses> => {
|
||||
try {
|
||||
const tagOnResponse = await prisma.tagsOnResponses.create({
|
||||
data: {
|
||||
@@ -21,7 +20,7 @@ export const addTagToRespone = async (responseId: string, tagId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteTagOnResponse = async (responseId: string, tagId: string) => {
|
||||
export const deleteTagOnResponse = async (responseId: string, tagId: string): Promise<TTagsOnResponses> => {
|
||||
try {
|
||||
const deletedTag = await prisma.tagsOnResponses.delete({
|
||||
where: {
|
||||
@@ -37,7 +36,7 @@ export const deleteTagOnResponse = async (responseId: string, tagId: string) =>
|
||||
}
|
||||
};
|
||||
|
||||
export const getTagsOnResponsesCount = cache(async (): Promise<TTagsCount> => {
|
||||
export const getTagsOnResponsesCount = async (): Promise<TTagsCount> => {
|
||||
try {
|
||||
const tagsCount = await prisma.tagsOnResponses.groupBy({
|
||||
by: ["tagId"],
|
||||
@@ -50,4 +49,4 @@ export const getTagsOnResponsesCount = cache(async (): Promise<TTagsCount> => {
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { prisma } from "@formbricks/database";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { DatabaseError, ResourceNotFoundError, ValidationError } from "@formbricks/types/v1/errors";
|
||||
import { TTeam, TTeamUpdateInput } from "@formbricks/types/v1/teams";
|
||||
import { TProductUpdateInput } from "@formbricks/types/v1/product";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { revalidateTag, unstable_cache } from "next/cache";
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
} from "../utils/createDemoProductHelpers";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { getEnvironmentCacheTag } from "../environment/service";
|
||||
|
||||
export const select = {
|
||||
id: true,
|
||||
@@ -158,7 +160,7 @@ export const updateTeam = async (teamId: string, data: Partial<TTeamUpdateInput>
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteTeam = async (teamId: string) => {
|
||||
export const deleteTeam = async (teamId: string): Promise<TTeam> => {
|
||||
validateInputs([teamId, ZId]);
|
||||
try {
|
||||
const deletedTeam = await prisma.team.delete({
|
||||
@@ -177,6 +179,7 @@ export const deleteTeam = async (teamId: string) => {
|
||||
deletedTeam?.products.forEach((product) => {
|
||||
product.environments.forEach((environment) => {
|
||||
revalidateTag(getTeamByEnvironmentIdCacheTag(environment.id));
|
||||
revalidateTag(getEnvironmentCacheTag(environment.id));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -196,7 +199,7 @@ export const deleteTeam = async (teamId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const createDemoProduct = async (teamId: string) => {
|
||||
export const createDemoProduct = async (teamId: string): Promise<TProductUpdateInput> => {
|
||||
validateInputs([teamId, ZId]);
|
||||
|
||||
const demoProduct = await prisma.product.create({
|
||||
@@ -332,7 +335,7 @@ export const createDemoProduct = async (teamId: string) => {
|
||||
})),
|
||||
}),
|
||||
]);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
|
||||
export const getTeamDetails = async (environmentId: string) => {
|
||||
export const getTeamDetails = async (
|
||||
environmentId: string
|
||||
): Promise<{ teamId: string; teamOwnerId: string | undefined }> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const environment = await prisma.environment.findUnique({
|
||||
|
||||
@@ -5,10 +5,9 @@ import { prisma } from "@formbricks/database";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { cache } from "react";
|
||||
import { ResourceNotFoundError, DatabaseError, InvalidInputError } from "@formbricks/types/v1/errors";
|
||||
|
||||
export const getWebhooks = cache(async (environmentId: string): Promise<TWebhook[]> => {
|
||||
export const getWebhooks = async (environmentId: string): Promise<TWebhook[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const webhooks = await prisma.webhook.findMany({
|
||||
@@ -20,7 +19,7 @@ export const getWebhooks = cache(async (environmentId: string): Promise<TWebhook
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching webhooks for environment ${environmentId}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getCountOfWebhooksBasedOnSource = async (
|
||||
environmentId: string,
|
||||
|
||||
@@ -10,3 +10,12 @@ export const ZAction = z.object({
|
||||
});
|
||||
|
||||
export type TAction = z.infer<typeof ZAction>;
|
||||
|
||||
export const ZActionInput = z.object({
|
||||
environmentId: z.string().cuid2(),
|
||||
sessionId: z.string().cuid2(),
|
||||
name: z.string(),
|
||||
properties: z.record(z.string()),
|
||||
});
|
||||
|
||||
export type TActionInput = z.infer<typeof ZActionInput>;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const ZString = z.string();
|
||||
export const ZNumber = z.number();
|
||||
export const ZOptionalNumber = z.number().optional();
|
||||
export const ZColor = z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/);
|
||||
export const ZSurveyPlacement = z.enum(["bottomLeft", "bottomRight", "topLeft", "topRight", "center"]);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { ZEnvironment } from "./environment";
|
||||
|
||||
// Define a specific schema for googleSheets config
|
||||
|
||||
/* GOOGLE SHEETS CONFIGURATIONS */
|
||||
export const ZGoogleCredential = z.object({
|
||||
scope: z.string(),
|
||||
token_type: z.literal("Bearer"),
|
||||
@@ -10,11 +8,13 @@ export const ZGoogleCredential = z.object({
|
||||
access_token: z.string(),
|
||||
refresh_token: z.string(),
|
||||
});
|
||||
export type TGoogleCredential = z.infer<typeof ZGoogleCredential>;
|
||||
|
||||
export const ZGoogleSpreadsheet = z.object({
|
||||
name: z.string(),
|
||||
id: z.string(),
|
||||
});
|
||||
export type TGoogleSpreadsheet = z.infer<typeof ZGoogleSpreadsheet>;
|
||||
|
||||
export const ZGoogleSheetsConfigData = z.object({
|
||||
createdAt: z.date(),
|
||||
@@ -25,26 +25,14 @@ export const ZGoogleSheetsConfigData = z.object({
|
||||
surveyId: z.string(),
|
||||
surveyName: z.string(),
|
||||
});
|
||||
export type TGoogleSheetsConfigData = z.infer<typeof ZGoogleSheetsConfigData>;
|
||||
|
||||
const ZGoogleSheetsConfig = z.object({
|
||||
key: ZGoogleCredential,
|
||||
data: z.array(ZGoogleSheetsConfigData),
|
||||
email: z.string(),
|
||||
});
|
||||
|
||||
// Define a dynamic schema for config based on integration type
|
||||
const ZPlaceholderConfig = z.object({
|
||||
placeholder: z.string(),
|
||||
});
|
||||
|
||||
export const ZIntegrationConfig = z.union([ZGoogleSheetsConfig, ZPlaceholderConfig]);
|
||||
|
||||
export const ZIntegration = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(["googleSheets", "placeholder"]),
|
||||
environmentId: z.string(),
|
||||
config: ZIntegrationConfig,
|
||||
});
|
||||
export type TGoogleSheetsConfig = z.infer<typeof ZGoogleSheetsConfig>;
|
||||
|
||||
export const ZGoogleSheetIntegration = z.object({
|
||||
id: z.string(),
|
||||
@@ -52,20 +40,23 @@ export const ZGoogleSheetIntegration = z.object({
|
||||
environmentId: z.string(),
|
||||
config: ZGoogleSheetsConfig,
|
||||
});
|
||||
|
||||
export const ZPlaceHolderIntegration = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(["placeholder"]),
|
||||
environmentId: z.string(),
|
||||
config: ZPlaceholderConfig,
|
||||
environment: ZEnvironment,
|
||||
});
|
||||
|
||||
export type TIntegration = z.infer<typeof ZIntegration>;
|
||||
export type TIntegrationConfig = z.infer<typeof ZIntegrationConfig>;
|
||||
export type TGoogleCredential = z.infer<typeof ZGoogleCredential>;
|
||||
export type TGoogleSpreadsheet = z.infer<typeof ZGoogleSpreadsheet>;
|
||||
export type TGoogleSheetsConfig = z.infer<typeof ZGoogleSheetsConfig>;
|
||||
export type TGoogleSheetsConfigData = z.infer<typeof ZGoogleSheetsConfigData>;
|
||||
export type TGoogleSheetIntegration = z.infer<typeof ZGoogleSheetIntegration>;
|
||||
export type TPlaceHolderIntegration = z.infer<typeof ZPlaceHolderIntegration>;
|
||||
|
||||
// Define a specific schema for integration configs
|
||||
// When we add other configurations it will be z.union([ZGoogleSheetsConfig, ZSlackConfig, ...])
|
||||
export const ZIntegrationConfig = ZGoogleSheetsConfig;
|
||||
export type TIntegrationConfig = z.infer<typeof ZIntegrationConfig>;
|
||||
|
||||
export const ZIntegration = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(["googleSheets"]),
|
||||
environmentId: z.string(),
|
||||
config: ZIntegrationConfig,
|
||||
});
|
||||
export type TIntegration = z.infer<typeof ZIntegration>;
|
||||
|
||||
export const ZIntegrationInput = z.object({
|
||||
type: z.enum(["googleSheets"]),
|
||||
config: ZIntegrationConfig,
|
||||
});
|
||||
export type TIntegrationInput = z.infer<typeof ZIntegrationInput>;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import z from "zod";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { ZMembershipRole } from "./memberships";
|
||||
|
||||
const ZInvite = z.object({
|
||||
@@ -14,6 +13,22 @@ const ZInvite = z.object({
|
||||
expiresAt: z.date(),
|
||||
role: ZMembershipRole,
|
||||
});
|
||||
|
||||
export type TInvite = z.infer<typeof ZInvite>;
|
||||
export type TInviteUpdateInput = Prisma.InviteUpdateInput;
|
||||
|
||||
export const ZInvitee = z.object({
|
||||
email: z.string(),
|
||||
name: z.string().nullable(),
|
||||
role: ZMembershipRole,
|
||||
});
|
||||
export type TInvitee = z.infer<typeof ZInvitee>;
|
||||
|
||||
export const ZCurrentUser = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().nullable(),
|
||||
});
|
||||
export type TCurrentUser = z.infer<typeof ZCurrentUser>;
|
||||
|
||||
export const ZInviteUpdateInput = z.object({
|
||||
role: ZMembershipRole,
|
||||
});
|
||||
export type TInviteUpdateInput = z.infer<typeof ZInviteUpdateInput>;
|
||||
|
||||
@@ -48,12 +48,3 @@ export const ZJsPeopleAttributeInput = z.object({
|
||||
});
|
||||
|
||||
export type TJsPeopleAttributeInput = z.infer<typeof ZJsPeopleAttributeInput>;
|
||||
|
||||
export const ZJsActionInput = z.object({
|
||||
environmentId: z.string().cuid2(),
|
||||
sessionId: z.string().cuid2(),
|
||||
name: z.string(),
|
||||
properties: z.record(z.string()),
|
||||
});
|
||||
|
||||
export type TJsActionInput = z.infer<typeof ZJsActionInput>;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import z from "zod";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
export const ZMembershipRole = z.enum(["owner", "admin", "editor", "developer", "viewer"]);
|
||||
|
||||
@@ -24,4 +23,7 @@ export const ZMember = z.object({
|
||||
|
||||
export type TMember = z.infer<typeof ZMember>;
|
||||
|
||||
export type TMembershipUpdateInput = Prisma.MembershipUpdateInput;
|
||||
export const ZMembershipUpdateInput = z.object({
|
||||
role: ZMembershipRole,
|
||||
});
|
||||
export type TMembershipUpdateInput = z.infer<typeof ZMembershipUpdateInput>;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import { ZActionClass } from "./actionClasses";
|
||||
import { QuestionType } from "../questions";
|
||||
import { ZColor, ZSurveyPlacement } from "./common";
|
||||
|
||||
@@ -276,7 +275,7 @@ export const ZSurvey = z.object({
|
||||
attributeFilters: z.array(ZSurveyAttributeFilter),
|
||||
displayOption: ZSurveyDisplayOption,
|
||||
autoClose: z.number().nullable(),
|
||||
triggers: z.array(ZActionClass),
|
||||
triggers: z.array(z.string()),
|
||||
redirectUrl: z.string().url().nullable(),
|
||||
recontactDays: z.number().nullable(),
|
||||
questions: ZSurveyQuestions,
|
||||
@@ -293,7 +292,6 @@ export const ZSurvey = z.object({
|
||||
export const ZSurveyInput = z.object({
|
||||
name: z.string(),
|
||||
type: ZSurveyType.optional(),
|
||||
environmentId: z.string(),
|
||||
status: ZSurveyStatus.optional(),
|
||||
displayOption: ZSurveyDisplayOption.optional(),
|
||||
autoClose: z.number().optional(),
|
||||
@@ -306,9 +304,8 @@ export const ZSurveyInput = z.object({
|
||||
closeOnDate: z.date().optional(),
|
||||
surveyClosedMessage: ZSurveyClosedMessage.optional(),
|
||||
verifyEmail: ZSurveyVerifyEmail.optional(),
|
||||
// TODO: Update survey create endpoint to accept attributeFilters and triggers like the survey update endpoint
|
||||
// attributeFilters: z.array(ZSurveyAttributeFilter).optional(),
|
||||
//triggers: z.array(ZActionClass).optional(),
|
||||
attributeFilters: z.array(ZSurveyAttributeFilter).optional(),
|
||||
triggers: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
export type TSurvey = z.infer<typeof ZSurvey>;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export type TTag = z.infer<typeof ZTag>;
|
||||
|
||||
export const ZTag = z.object({
|
||||
id: z.string().cuid2(),
|
||||
createdAt: z.date(),
|
||||
@@ -9,8 +7,7 @@ export const ZTag = z.object({
|
||||
name: z.string(),
|
||||
environmentId: z.string(),
|
||||
});
|
||||
|
||||
export type TTagsCount = z.infer<typeof ZTagsCount>;
|
||||
export type TTag = z.infer<typeof ZTag>;
|
||||
|
||||
export const ZTagsCount = z.array(
|
||||
z.object({
|
||||
@@ -18,3 +15,10 @@ export const ZTagsCount = z.array(
|
||||
count: z.number(),
|
||||
})
|
||||
);
|
||||
export type TTagsCount = z.infer<typeof ZTagsCount>;
|
||||
|
||||
export const ZTagsOnResponses = z.object({
|
||||
responseId: z.string(),
|
||||
tagId: z.string(),
|
||||
});
|
||||
export type TTagsOnResponses = z.infer<typeof ZTagsOnResponses>;
|
||||
|
||||
Reference in New Issue
Block a user