mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-08 16:21:09 -06:00
chore: add react cache to improve render performance (#2867)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import z from "zod";
|
||||
import { z } from "zod";
|
||||
|
||||
export const ZActionProperties = z.record(z.string());
|
||||
export { ZActionClassNoCodeConfig } from "@formbricks/types/actionClasses";
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Users2Icon,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import z from "zod";
|
||||
import { z } from "zod";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone";
|
||||
import {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TAction, TActionInput, ZActionInput } from "@formbricks/types/actions";
|
||||
import { ZOptionalNumber } from "@formbricks/types/common";
|
||||
@@ -16,96 +17,100 @@ import { validateInputs } from "../utils/validate";
|
||||
import { actionCache } from "./cache";
|
||||
import { getStartDateOfLastMonth, getStartDateOfLastQuarter, getStartDateOfLastWeek } from "./utils";
|
||||
|
||||
export const getActionsByPersonId = (personId: string, page?: number): Promise<TAction[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
export const getActionsByPersonId = reactCache(
|
||||
async (personId: string, page?: number): Promise<TAction[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const actionsPrisma = await prisma.action.findMany({
|
||||
where: {
|
||||
person: {
|
||||
id: personId,
|
||||
try {
|
||||
const actionsPrisma = await prisma.action.findMany({
|
||||
where: {
|
||||
person: {
|
||||
id: personId,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
include: {
|
||||
actionClass: true,
|
||||
},
|
||||
});
|
||||
|
||||
return actionsPrisma.map((action) => ({
|
||||
id: action.id,
|
||||
createdAt: action.createdAt,
|
||||
personId: action.personId,
|
||||
properties: action.properties,
|
||||
actionClass: action.actionClass,
|
||||
}));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionsByPersonId-${personId}-${page}`],
|
||||
{
|
||||
tags: [actionCache.tag.byPersonId(personId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getActionsByEnvironmentId = (environmentId: string, page?: number): Promise<TAction[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const actionsPrisma = await prisma.action.findMany({
|
||||
where: {
|
||||
actionClass: {
|
||||
environmentId: environmentId,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
include: {
|
||||
actionClass: true,
|
||||
},
|
||||
});
|
||||
const actions: TAction[] = [];
|
||||
// transforming response to type TAction[]
|
||||
actionsPrisma.forEach((action) => {
|
||||
actions.push({
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
include: {
|
||||
actionClass: true,
|
||||
},
|
||||
});
|
||||
|
||||
return actionsPrisma.map((action) => ({
|
||||
id: action.id,
|
||||
createdAt: action.createdAt,
|
||||
// sessionId: action.sessionId,
|
||||
personId: action.personId,
|
||||
properties: action.properties,
|
||||
actionClass: action.actionClass,
|
||||
});
|
||||
});
|
||||
return actions;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
}));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionsByPersonId-${personId}-${page}`],
|
||||
{
|
||||
tags: [actionCache.tag.byPersonId(personId)],
|
||||
}
|
||||
},
|
||||
[`getActionsByEnvironmentId-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [actionCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getActionsByEnvironmentId = reactCache(
|
||||
async (environmentId: string, page?: number): Promise<TAction[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const actionsPrisma = await prisma.action.findMany({
|
||||
where: {
|
||||
actionClass: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
include: {
|
||||
actionClass: true,
|
||||
},
|
||||
});
|
||||
const actions: TAction[] = [];
|
||||
// transforming response to type TAction[]
|
||||
actionsPrisma.forEach((action) => {
|
||||
actions.push({
|
||||
id: action.id,
|
||||
createdAt: action.createdAt,
|
||||
// sessionId: action.sessionId,
|
||||
personId: action.personId,
|
||||
properties: action.properties,
|
||||
actionClass: action.actionClass,
|
||||
});
|
||||
});
|
||||
return actions;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionsByEnvironmentId-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [actionCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const createAction = async (data: TActionInput): Promise<TAction> => {
|
||||
validateInputs([data, ZActionInput]);
|
||||
@@ -165,255 +170,273 @@ export const createAction = async (data: TActionInput): Promise<TAction> => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getActionCountInLastHour = async (actionClassId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
export const getActionCountInLastHour = reactCache(
|
||||
async (actionClassId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
|
||||
try {
|
||||
const numEventsLastHour = await prisma.action.count({
|
||||
where: {
|
||||
actionClassId: actionClassId,
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 60 * 60 * 1000),
|
||||
try {
|
||||
const numEventsLastHour = await prisma.action.count({
|
||||
where: {
|
||||
actionClassId: actionClassId,
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 60 * 60 * 1000),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return numEventsLastHour;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
});
|
||||
return numEventsLastHour;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastHour-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastHour-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getActionCountInLast24Hours = async (actionClassId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
export const getActionCountInLast24Hours = reactCache(
|
||||
async (actionClassId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
|
||||
try {
|
||||
const numEventsLast24Hours = await prisma.action.count({
|
||||
where: {
|
||||
actionClassId: actionClassId,
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
||||
try {
|
||||
const numEventsLast24Hours = await prisma.action.count({
|
||||
where: {
|
||||
actionClassId: actionClassId,
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return numEventsLast24Hours;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
});
|
||||
return numEventsLast24Hours;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionCountInLast24Hours-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getActionCountInLast24Hours-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getActionCountInLast7Days = async (actionClassId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
export const getActionCountInLast7Days = reactCache(
|
||||
async (actionClassId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
|
||||
try {
|
||||
const numEventsLast7Days = await prisma.action.count({
|
||||
where: {
|
||||
actionClassId: actionClassId,
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
|
||||
try {
|
||||
const numEventsLast7Days = await prisma.action.count({
|
||||
where: {
|
||||
actionClassId: actionClassId,
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return numEventsLast7Days;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
});
|
||||
return numEventsLast7Days;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionCountInLast7Days-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getActionCountInLast7Days-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getActionCountInLastQuarter = (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
export const getActionCountInLastQuarter = reactCache(
|
||||
async (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
|
||||
try {
|
||||
const numEventsLastQuarter = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
try {
|
||||
const numEventsLastQuarter = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
},
|
||||
createdAt: {
|
||||
gte: getStartDateOfLastQuarter(),
|
||||
},
|
||||
},
|
||||
createdAt: {
|
||||
gte: getStartDateOfLastQuarter(),
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return numEventsLastQuarter;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
return numEventsLastQuarter;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastQuarter-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastQuarter-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getActionCountInLastMonth = (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
export const getActionCountInLastMonth = reactCache(
|
||||
async (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
|
||||
try {
|
||||
const numEventsLastMonth = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
try {
|
||||
const numEventsLastMonth = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
},
|
||||
createdAt: {
|
||||
gte: getStartDateOfLastMonth(),
|
||||
},
|
||||
},
|
||||
createdAt: {
|
||||
gte: getStartDateOfLastMonth(),
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return numEventsLastMonth;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
return numEventsLastMonth;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastMonth-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastMonth-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getActionCountInLastWeek = (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
export const getActionCountInLastWeek = reactCache(
|
||||
async (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
|
||||
try {
|
||||
const numEventsLastWeek = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
try {
|
||||
const numEventsLastWeek = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
},
|
||||
createdAt: {
|
||||
gte: getStartDateOfLastWeek(),
|
||||
},
|
||||
},
|
||||
createdAt: {
|
||||
gte: getStartDateOfLastWeek(),
|
||||
},
|
||||
},
|
||||
});
|
||||
return numEventsLastWeek;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
});
|
||||
return numEventsLastWeek;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastWeek-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getActionCountInLastWeek-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getTotalOccurrencesForAction = (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
export const getTotalOccurrencesForAction = reactCache(
|
||||
async (actionClassId: string, personId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
|
||||
try {
|
||||
const count = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
try {
|
||||
const count = await prisma.action.count({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return count;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
return count;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getTotalOccurrencesForAction-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getTotalOccurrencesForAction-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getLastOccurrenceDaysAgo = (actionClassId: string, personId: string): Promise<number | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
export const getLastOccurrenceDaysAgo = reactCache(
|
||||
async (actionClassId: string, personId: string): Promise<number | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
|
||||
try {
|
||||
const lastEvent = await prisma.action.findFirst({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
try {
|
||||
const lastEvent = await prisma.action.findFirst({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!lastEvent) return null;
|
||||
return differenceInDays(new Date(), lastEvent.createdAt);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getLastOccurrenceDaysAgo-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getFirstOccurrenceDaysAgo = (actionClassId: string, personId: string): Promise<number | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
|
||||
try {
|
||||
const firstEvent = await prisma.action.findFirst({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "asc",
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
select: {
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!firstEvent) return null;
|
||||
return differenceInDays(new Date(), firstEvent.createdAt);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
if (!lastEvent) return null;
|
||||
return differenceInDays(new Date(), lastEvent.createdAt);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getLastOccurrenceDaysAgo-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getFirstOccurrenceDaysAgo-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getFirstOccurrenceDaysAgo = reactCache(
|
||||
async (actionClassId: string, personId: string): Promise<number | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [personId, ZId]);
|
||||
|
||||
try {
|
||||
const firstEvent = await prisma.action.findFirst({
|
||||
where: {
|
||||
personId,
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "asc",
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!firstEvent) return null;
|
||||
return differenceInDays(new Date(), firstEvent.createdAt);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getFirstOccurrenceDaysAgo-${actionClassId}-${personId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TActionClass, TActionClassInput, ZActionClassInput } from "@formbricks/types/actionClasses";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
@@ -24,85 +25,88 @@ const selectActionClass = {
|
||||
environmentId: true,
|
||||
} satisfies Prisma.ActionClassSelect;
|
||||
|
||||
export const getActionClasses = (environmentId: string, page?: number): Promise<TActionClass[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
export const getActionClasses = reactCache(
|
||||
async (environmentId: string, page?: number): Promise<TActionClass[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
return await prisma.actionClass.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
select: selectActionClass,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
orderBy: {
|
||||
createdAt: "asc",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching actions for environment ${environmentId}`);
|
||||
try {
|
||||
return await prisma.actionClass.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
select: selectActionClass,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
orderBy: {
|
||||
createdAt: "asc",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching actions for environment ${environmentId}`);
|
||||
}
|
||||
},
|
||||
[`getActionClasses-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getActionClasses-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
// This function is used to get an action by its name and environmentId(it can return private actions as well)
|
||||
export const getActionClassByEnvironmentIdAndName = (
|
||||
environmentId: string,
|
||||
name: string
|
||||
): Promise<TActionClass | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [name, ZString]);
|
||||
export const getActionClassByEnvironmentIdAndName = reactCache(
|
||||
async (environmentId: string, name: string): Promise<TActionClass | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [name, ZString]);
|
||||
|
||||
try {
|
||||
const actionClass = await prisma.actionClass.findFirst({
|
||||
where: {
|
||||
name,
|
||||
environmentId,
|
||||
},
|
||||
select: selectActionClass,
|
||||
});
|
||||
try {
|
||||
const actionClass = await prisma.actionClass.findFirst({
|
||||
where: {
|
||||
name,
|
||||
environmentId,
|
||||
},
|
||||
select: selectActionClass,
|
||||
});
|
||||
|
||||
return actionClass;
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching action`);
|
||||
return actionClass;
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching action`);
|
||||
}
|
||||
},
|
||||
[`getActionClassByEnvironmentIdAndName-${environmentId}-${name}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byNameAndEnvironmentId(name, environmentId)],
|
||||
}
|
||||
},
|
||||
[`getActionClassByEnvironmentIdAndName-${environmentId}-${name}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byNameAndEnvironmentId(name, environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getActionClass = (actionClassId: string): Promise<TActionClass | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
export const getActionClass = reactCache(
|
||||
async (actionClassId: string): Promise<TActionClass | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId]);
|
||||
|
||||
try {
|
||||
const actionClass = await prisma.actionClass.findUnique({
|
||||
where: {
|
||||
id: actionClassId,
|
||||
},
|
||||
select: selectActionClass,
|
||||
});
|
||||
try {
|
||||
const actionClass = await prisma.actionClass.findUnique({
|
||||
where: {
|
||||
id: actionClassId,
|
||||
},
|
||||
select: selectActionClass,
|
||||
});
|
||||
|
||||
return actionClass;
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching action`);
|
||||
return actionClass;
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching action`);
|
||||
}
|
||||
},
|
||||
[`getActionClass-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
},
|
||||
[`getActionClass-${actionClassId}`],
|
||||
{
|
||||
tags: [actionClassCache.tag.byId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const deleteActionClass = async (
|
||||
environmentId: string,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { createHash, randomBytes } from "crypto";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TApiKey, TApiKeyCreateInput, ZApiKeyCreateInput } from "@formbricks/types/apiKeys";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
@@ -12,64 +13,68 @@ import { getHash } from "../crypto";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { apiKeyCache } from "./cache";
|
||||
|
||||
export const getApiKey = (apiKeyId: string): Promise<TApiKey | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([apiKeyId, ZString]);
|
||||
export const getApiKey = reactCache(
|
||||
(apiKeyId: string): Promise<TApiKey | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([apiKeyId, ZString]);
|
||||
|
||||
if (!apiKeyId) {
|
||||
throw new InvalidInputError("API key cannot be null or undefined.");
|
||||
}
|
||||
|
||||
try {
|
||||
const apiKeyData = await prisma.apiKey.findUnique({
|
||||
where: {
|
||||
id: apiKeyId,
|
||||
},
|
||||
});
|
||||
|
||||
return apiKeyData;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
if (!apiKeyId) {
|
||||
throw new InvalidInputError("API key cannot be null or undefined.");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getApiKey-${apiKeyId}`],
|
||||
{
|
||||
tags: [apiKeyCache.tag.byId(apiKeyId)],
|
||||
}
|
||||
)();
|
||||
try {
|
||||
const apiKeyData = await prisma.apiKey.findUnique({
|
||||
where: {
|
||||
id: apiKeyId,
|
||||
},
|
||||
});
|
||||
|
||||
export const getApiKeys = (environmentId: string, page?: number): Promise<TApiKey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
return apiKeyData;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
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;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getApiKey-${apiKeyId}`],
|
||||
{
|
||||
tags: [apiKeyCache.tag.byId(apiKeyId)],
|
||||
}
|
||||
},
|
||||
[`getApiKeys-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [apiKeyCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getApiKeys = reactCache(
|
||||
(environmentId: string, page?: number): Promise<TApiKey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
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;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getApiKeys-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [apiKeyCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const hashApiKey = (key: string): string => createHash("sha256").update(key).digest("hex");
|
||||
|
||||
@@ -105,9 +110,8 @@ export const createApiKey = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getApiKeyFromKey = (apiKey: string): Promise<TApiKey | null> => {
|
||||
export const getApiKeyFromKey = reactCache((apiKey: string): Promise<TApiKey | null> => {
|
||||
const hashedKey = getHash(apiKey);
|
||||
|
||||
return cache(
|
||||
async () => {
|
||||
validateInputs([apiKey, ZString]);
|
||||
@@ -137,7 +141,7 @@ export const getApiKeyFromKey = (apiKey: string): Promise<TApiKey | null> => {
|
||||
tags: [apiKeyCache.tag.byHashedKey(hashedKey)],
|
||||
}
|
||||
)();
|
||||
};
|
||||
});
|
||||
|
||||
export const deleteApiKey = async (id: string): Promise<TApiKey | null> => {
|
||||
validateInputs([id, ZId]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TAttributes, ZAttributes } from "@formbricks/types/attributes";
|
||||
import { ZString } from "@formbricks/types/common";
|
||||
@@ -38,67 +39,71 @@ const convertPrismaAttributes = (prismaAttributes: any): TAttributes => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getAttributes = (personId: string): Promise<TAttributes> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId]);
|
||||
export const getAttributes = reactCache(
|
||||
(personId: string): Promise<TAttributes> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId]);
|
||||
|
||||
try {
|
||||
const prismaAttributes = await prisma.attribute.findMany({
|
||||
where: {
|
||||
personId,
|
||||
},
|
||||
select: selectAttribute,
|
||||
});
|
||||
try {
|
||||
const prismaAttributes = await prisma.attribute.findMany({
|
||||
where: {
|
||||
personId,
|
||||
},
|
||||
select: selectAttribute,
|
||||
});
|
||||
|
||||
return convertPrismaAttributes(prismaAttributes);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return convertPrismaAttributes(prismaAttributes);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getAttributes-${personId}`],
|
||||
{
|
||||
tags: [attributeCache.tag.byPersonId(personId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const getAttributesByUserId = reactCache(
|
||||
(environmentId: string, userId: string): Promise<TAttributes> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [userId, ZString]);
|
||||
|
||||
const person = await getPersonByUserId(environmentId, userId);
|
||||
|
||||
if (!person) {
|
||||
throw new Error("Person not found");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getAttributes-${personId}`],
|
||||
{
|
||||
tags: [attributeCache.tag.byPersonId(personId)],
|
||||
}
|
||||
)();
|
||||
try {
|
||||
const prismaAttributes = await prisma.attribute.findMany({
|
||||
where: {
|
||||
personId: person.id,
|
||||
},
|
||||
select: selectAttribute,
|
||||
});
|
||||
|
||||
export const getAttributesByUserId = (environmentId: string, userId: string): Promise<TAttributes> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [userId, ZString]);
|
||||
return convertPrismaAttributes(prismaAttributes);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
const person = await getPersonByUserId(environmentId, userId);
|
||||
|
||||
if (!person) {
|
||||
throw new Error("Person not found");
|
||||
}
|
||||
|
||||
try {
|
||||
const prismaAttributes = await prisma.attribute.findMany({
|
||||
where: {
|
||||
personId: person.id,
|
||||
},
|
||||
select: selectAttribute,
|
||||
});
|
||||
|
||||
return convertPrismaAttributes(prismaAttributes);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getAttributesByUserId-${environmentId}-${userId}`],
|
||||
{
|
||||
tags: [attributeCache.tag.byEnvironmentIdAndUserId(environmentId, userId)],
|
||||
}
|
||||
},
|
||||
[`getAttributesByUserId-${environmentId}-${userId}`],
|
||||
{
|
||||
tags: [attributeCache.tag.byEnvironmentIdAndUserId(environmentId, userId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getAttribute = (name: string, personId: string): Promise<string | undefined> =>
|
||||
cache(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import {
|
||||
TAttributeClass,
|
||||
@@ -18,68 +19,72 @@ import { ITEMS_PER_PAGE, MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT } from "../consta
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { attributeClassCache } from "./cache";
|
||||
|
||||
export const getAttributeClass = async (attributeClassId: string): Promise<TAttributeClass | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([attributeClassId, ZId]);
|
||||
export const getAttributeClass = reactCache(
|
||||
async (attributeClassId: string): Promise<TAttributeClass | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([attributeClassId, ZId]);
|
||||
|
||||
try {
|
||||
const attributeClass = await prisma.attributeClass.findFirst({
|
||||
where: {
|
||||
id: attributeClassId,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const attributeClass = await prisma.attributeClass.findFirst({
|
||||
where: {
|
||||
id: attributeClassId,
|
||||
},
|
||||
});
|
||||
|
||||
return attributeClass;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getAttributeClass-${attributeClassId}`],
|
||||
{
|
||||
tags: [attributeClassCache.tag.byId(attributeClassId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getAttributeClasses = async (environmentId: string, page?: number): Promise<TAttributeClass[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
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,
|
||||
});
|
||||
|
||||
return attributeClasses.filter((attributeClass) => {
|
||||
if (attributeClass.name === "userId" && attributeClass.type === "automatic") {
|
||||
return false;
|
||||
return attributeClass;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getAttributeClass-${attributeClassId}`],
|
||||
{
|
||||
tags: [attributeClassCache.tag.byId(attributeClassId)],
|
||||
}
|
||||
},
|
||||
[`getAttributeClasses-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [attributeClassCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getAttributeClasses = reactCache(
|
||||
async (environmentId: string, page?: number): Promise<TAttributeClass[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
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,
|
||||
});
|
||||
|
||||
return attributeClasses.filter((attributeClass) => {
|
||||
if (attributeClass.name === "userId" && attributeClass.type === "automatic") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getAttributeClasses-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [attributeClassCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateAttributeClass = async (
|
||||
attributeClassId: string,
|
||||
@@ -113,7 +118,7 @@ export const updateAttributeClass = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getAttributeClassByName = async (environmentId: string, name: string) =>
|
||||
export const getAttributeClassByName = reactCache((environmentId: string, name: string) =>
|
||||
cache(
|
||||
async (): Promise<TAttributeClass | null> => {
|
||||
validateInputs([environmentId, ZId], [name, ZString]);
|
||||
@@ -138,7 +143,8 @@ export const getAttributeClassByName = async (environmentId: string, name: strin
|
||||
{
|
||||
tags: [attributeClassCache.tag.byEnvironmentIdAndName(environmentId, name)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const createAttributeClass = async (
|
||||
environmentId: string,
|
||||
@@ -208,26 +214,28 @@ export const deleteAttributeClass = async (attributeClassId: string): Promise<TA
|
||||
}
|
||||
};
|
||||
|
||||
export const getAttributeClassesCount = async (environmentId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getAttributeClassesCount = reactCache(
|
||||
async (environmentId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
return prisma.attributeClass.count({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
return prisma.attributeClass.count({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getAttributeClassesCount-${environmentId}`],
|
||||
{
|
||||
tags: [attributeClassCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getAttributeClassesCount-${environmentId}`],
|
||||
{
|
||||
tags: [attributeClassCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber } from "@formbricks/types/common";
|
||||
import {
|
||||
@@ -33,33 +34,35 @@ export const selectDisplay = {
|
||||
status: true,
|
||||
};
|
||||
|
||||
export const getDisplay = (displayId: string): Promise<TDisplay | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([displayId, ZId]);
|
||||
export const getDisplay = reactCache(
|
||||
async (displayId: string): Promise<TDisplay | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([displayId, ZId]);
|
||||
|
||||
try {
|
||||
const display = await prisma.display.findUnique({
|
||||
where: {
|
||||
id: displayId,
|
||||
},
|
||||
select: selectDisplay,
|
||||
});
|
||||
try {
|
||||
const display = await prisma.display.findUnique({
|
||||
where: {
|
||||
id: displayId,
|
||||
},
|
||||
select: selectDisplay,
|
||||
});
|
||||
|
||||
return display;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return display;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getDisplay-${displayId}`],
|
||||
{
|
||||
tags: [displayCache.tag.byId(displayId)],
|
||||
}
|
||||
},
|
||||
[`getDisplay-${displayId}`],
|
||||
{
|
||||
tags: [displayCache.tag.byId(displayId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateDisplay = async (
|
||||
displayId: string,
|
||||
@@ -272,38 +275,40 @@ export const markDisplayRespondedLegacy = async (displayId: string): Promise<TDi
|
||||
}
|
||||
};
|
||||
|
||||
export const getDisplaysByPersonId = (personId: string, page?: number): Promise<TDisplay[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
export const getDisplaysByPersonId = reactCache(
|
||||
async (personId: string, page?: number): Promise<TDisplay[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const displays = await prisma.display.findMany({
|
||||
where: {
|
||||
personId: personId,
|
||||
},
|
||||
select: selectDisplay,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
try {
|
||||
const displays = await prisma.display.findMany({
|
||||
where: {
|
||||
personId: personId,
|
||||
},
|
||||
select: selectDisplay,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return displays;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return displays;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getDisplaysByPersonId-${personId}-${page}`],
|
||||
{
|
||||
tags: [displayCache.tag.byPersonId(personId)],
|
||||
}
|
||||
},
|
||||
[`getDisplaysByPersonId-${personId}-${page}`],
|
||||
{
|
||||
tags: [displayCache.tag.byPersonId(personId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const deleteDisplayByResponseId = async (
|
||||
responseId: string,
|
||||
@@ -333,34 +338,36 @@ export const deleteDisplayByResponseId = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getDisplayCountBySurveyId = (surveyId: string, filters?: TDisplayFilters): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
export const getDisplayCountBySurveyId = reactCache(
|
||||
(surveyId: string, filters?: TDisplayFilters): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
|
||||
try {
|
||||
const displayCount = await prisma.display.count({
|
||||
where: {
|
||||
surveyId: surveyId,
|
||||
...(filters &&
|
||||
filters.createdAt && {
|
||||
createdAt: {
|
||||
gte: filters.createdAt.min,
|
||||
lte: filters.createdAt.max,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
return displayCount;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
const displayCount = await prisma.display.count({
|
||||
where: {
|
||||
surveyId: surveyId,
|
||||
...(filters &&
|
||||
filters.createdAt && {
|
||||
createdAt: {
|
||||
gte: filters.createdAt.min,
|
||||
lte: filters.createdAt.max,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
return displayCount;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getDisplayCountBySurveyId-${surveyId}-${JSON.stringify(filters)}`],
|
||||
{
|
||||
tags: [displayCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
},
|
||||
[`getDisplayCountBySurveyId-${surveyId}-${JSON.stringify(filters)}`],
|
||||
{
|
||||
tags: [displayCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import type {
|
||||
@@ -20,78 +21,82 @@ import { getProducts } from "../product/service";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { environmentCache } from "./cache";
|
||||
|
||||
export const getEnvironment = (environmentId: string): Promise<TEnvironment | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getEnvironment = reactCache(
|
||||
(environmentId: string): Promise<TEnvironment | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const environment = await prisma.environment.findUnique({
|
||||
where: {
|
||||
id: environmentId,
|
||||
},
|
||||
});
|
||||
return environment;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
const environment = await prisma.environment.findUnique({
|
||||
where: {
|
||||
id: environmentId,
|
||||
},
|
||||
});
|
||||
return environment;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getEnvironment-${environmentId}`],
|
||||
{
|
||||
tags: [environmentCache.tag.byId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const getEnvironments = reactCache(
|
||||
(productId: string): Promise<TEnvironment[]> =>
|
||||
cache(
|
||||
async (): Promise<TEnvironment[]> => {
|
||||
validateInputs([productId, ZId]);
|
||||
let productPrisma;
|
||||
try {
|
||||
productPrisma = await prisma.product.findFirst({
|
||||
where: {
|
||||
id: productId,
|
||||
},
|
||||
include: {
|
||||
environments: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!productPrisma) {
|
||||
throw new ResourceNotFoundError("Product", productId);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getEnvironment-${environmentId}`],
|
||||
{
|
||||
tags: [environmentCache.tag.byId(environmentId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getEnvironments = async (productId: string): Promise<TEnvironment[]> =>
|
||||
cache(
|
||||
async (): Promise<TEnvironment[]> => {
|
||||
validateInputs([productId, ZId]);
|
||||
let productPrisma;
|
||||
try {
|
||||
productPrisma = await prisma.product.findFirst({
|
||||
where: {
|
||||
id: productId,
|
||||
},
|
||||
include: {
|
||||
environments: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!productPrisma) {
|
||||
throw new ResourceNotFoundError("Product", productId);
|
||||
const environments: TEnvironment[] = [];
|
||||
for (let environment of productPrisma.environments) {
|
||||
let targetEnvironment: TEnvironment = ZEnvironment.parse(environment);
|
||||
environments.push(targetEnvironment);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const environments: TEnvironment[] = [];
|
||||
for (let environment of productPrisma.environments) {
|
||||
let targetEnvironment: TEnvironment = ZEnvironment.parse(environment);
|
||||
environments.push(targetEnvironment);
|
||||
}
|
||||
|
||||
try {
|
||||
return environments;
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
console.error(JSON.stringify(error.errors, null, 2));
|
||||
try {
|
||||
return environments;
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
console.error(JSON.stringify(error.errors, null, 2));
|
||||
}
|
||||
throw new ValidationError("Data validation of environments array failed");
|
||||
}
|
||||
throw new ValidationError("Data validation of environments array failed");
|
||||
},
|
||||
[`getEnvironments-${productId}`],
|
||||
{
|
||||
tags: [environmentCache.tag.byProductId(productId)],
|
||||
}
|
||||
},
|
||||
[`getEnvironments-${productId}`],
|
||||
{
|
||||
tags: [environmentCache.tag.byProductId(productId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateEnvironment = async (
|
||||
environmentId: string,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { cache } from "../cache";
|
||||
@@ -7,38 +8,42 @@ import { organizationCache } from "../organization/cache";
|
||||
import { userCache } from "../user/cache";
|
||||
|
||||
// Function to check if there are any users in the database
|
||||
export const getIsFreshInstance = (): Promise<boolean> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const userCount = await prisma.user.count();
|
||||
if (userCount === 0) return true;
|
||||
else return false;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
export const getIsFreshInstance = reactCache(
|
||||
(): Promise<boolean> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const userCount = await prisma.user.count();
|
||||
if (userCount === 0) return true;
|
||||
else return false;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
["getIsFreshInstance"],
|
||||
{ tags: [userCache.tag.byCount()] }
|
||||
)();
|
||||
},
|
||||
["getIsFreshInstance"],
|
||||
{ tags: [userCache.tag.byCount()] }
|
||||
)()
|
||||
);
|
||||
|
||||
// Function to check if there are any organizations in the database
|
||||
export const gethasNoOrganizations = (): Promise<boolean> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const organizationCount = await prisma.organization.count();
|
||||
return organizationCount === 0;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
export const gethasNoOrganizations = reactCache(
|
||||
(): Promise<boolean> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const organizationCount = await prisma.organization.count();
|
||||
return organizationCount === 0;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
["gethasNoOrganizations"],
|
||||
{ tags: [organizationCache.tag.byCount()] }
|
||||
)();
|
||||
},
|
||||
["gethasNoOrganizations"],
|
||||
{ tags: [organizationCache.tag.byCount()] }
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
@@ -48,86 +49,89 @@ export const createOrUpdateIntegration = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getIntegrations = (environmentId: string, page?: number): Promise<TIntegration[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
export const getIntegrations = reactCache(
|
||||
(environmentId: string, page?: number): Promise<TIntegration[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const integrations = await prisma.integration.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
return integrations;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getIntegrations-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [integrationCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getIntegration = (integrationId: string): Promise<TIntegration | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const integration = await prisma.integration.findUnique({
|
||||
where: {
|
||||
id: integrationId,
|
||||
},
|
||||
});
|
||||
return integration;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getIntegration-${integrationId}`],
|
||||
{
|
||||
tags: [integrationCache.tag.byId(integrationId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getIntegrationByType = (
|
||||
environmentId: string,
|
||||
type: TIntegrationInput["type"]
|
||||
): Promise<TIntegration | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [type, ZIntegrationType]);
|
||||
|
||||
try {
|
||||
const integration = await prisma.integration.findUnique({
|
||||
where: {
|
||||
type_environmentId: {
|
||||
try {
|
||||
const integrations = await prisma.integration.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
type,
|
||||
},
|
||||
},
|
||||
});
|
||||
return integration;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
return integrations;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getIntegrations-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [integrationCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getIntegrationByType-${environmentId}-${type}`],
|
||||
{
|
||||
tags: [integrationCache.tag.byEnvironmentIdAndType(environmentId, type)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getIntegration = reactCache(
|
||||
(integrationId: string): Promise<TIntegration | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const integration = await prisma.integration.findUnique({
|
||||
where: {
|
||||
id: integrationId,
|
||||
},
|
||||
});
|
||||
return integration;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getIntegration-${integrationId}`],
|
||||
{
|
||||
tags: [integrationCache.tag.byId(integrationId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const getIntegrationByType = reactCache(
|
||||
(environmentId: string, type: TIntegrationInput["type"]): Promise<TIntegration | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [type, ZIntegrationType]);
|
||||
|
||||
try {
|
||||
const integration = await prisma.integration.findUnique({
|
||||
where: {
|
||||
type_environmentId: {
|
||||
environmentId,
|
||||
type,
|
||||
},
|
||||
},
|
||||
});
|
||||
return integration;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getIntegrationByType-${environmentId}-${type}`],
|
||||
{
|
||||
tags: [integrationCache.tag.byEnvironmentIdAndType(environmentId, type)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const deleteIntegration = async (integrationId: string): Promise<TIntegration> => {
|
||||
validateInputs([integrationId, ZString]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import {
|
||||
@@ -41,33 +42,35 @@ interface InviteWithCreator extends TInvite {
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
export const getInvitesByOrganizationId = (organizationId: string, page?: number): Promise<TInvite[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZString], [page, ZOptionalNumber]);
|
||||
export const getInvitesByOrganizationId = reactCache(
|
||||
(organizationId: string, page?: number): Promise<TInvite[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZString], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const invites = await prisma.invite.findMany({
|
||||
where: { organizationId },
|
||||
select: inviteSelect,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
try {
|
||||
const invites = await prisma.invite.findMany({
|
||||
where: { organizationId },
|
||||
select: inviteSelect,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
return invites;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return invites;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getInvitesByOrganizationId-${organizationId}-${page}`],
|
||||
{
|
||||
tags: [inviteCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
},
|
||||
[`getInvitesByOrganizationId-${organizationId}-${page}`],
|
||||
{
|
||||
tags: [inviteCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateInvite = async (inviteId: string, data: TInviteUpdateInput): Promise<TInvite | null> => {
|
||||
validateInputs([inviteId, ZString], [data, ZInviteUpdateInput]);
|
||||
@@ -127,40 +130,42 @@ export const deleteInvite = async (inviteId: string): Promise<TInvite> => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getInvite = (inviteId: string): Promise<InviteWithCreator | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([inviteId, ZString]);
|
||||
export const getInvite = reactCache(
|
||||
(inviteId: string): Promise<InviteWithCreator | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([inviteId, ZString]);
|
||||
|
||||
try {
|
||||
const invite = await prisma.invite.findUnique({
|
||||
where: {
|
||||
id: inviteId,
|
||||
},
|
||||
include: {
|
||||
creator: {
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
try {
|
||||
const invite = await prisma.invite.findUnique({
|
||||
where: {
|
||||
id: inviteId,
|
||||
},
|
||||
include: {
|
||||
creator: {
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return invite;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return invite;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getInvite-${inviteId}`],
|
||||
{
|
||||
tags: [inviteCache.tag.byId(inviteId)],
|
||||
}
|
||||
},
|
||||
[`getInvite-${inviteId}`],
|
||||
{
|
||||
tags: [inviteCache.tag.byId(inviteId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const resendInvite = async (inviteId: string): Promise<TInvite> => {
|
||||
validateInputs([inviteId, ZString]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
import { DatabaseError, ValidationError } from "@formbricks/types/errors";
|
||||
@@ -58,7 +59,7 @@ export const createLanguage = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getSurveysUsingGivenLanguage = async (languageId: string): Promise<string[]> => {
|
||||
export const getSurveysUsingGivenLanguage = reactCache(async (languageId: string): Promise<string[]> => {
|
||||
try {
|
||||
// Check if the language is used in any survey
|
||||
const surveys = await prisma.surveyLanguage.findMany({
|
||||
@@ -84,7 +85,7 @@ export const getSurveysUsingGivenLanguage = async (languageId: string): Promise<
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export const deleteLanguage = async (environmentId: string, languageId: string): Promise<TLanguage> => {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import { DatabaseError, ResourceNotFoundError, UnknownError } from "@formbricks/types/errors";
|
||||
@@ -16,119 +17,122 @@ import { organizationCache } from "../organization/cache";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { membershipCache } from "./cache";
|
||||
|
||||
export const getMembersByOrganizationId = async (organizationId: string, page?: number): Promise<TMember[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZString], [page, ZOptionalNumber]);
|
||||
export const getMembersByOrganizationId = reactCache(
|
||||
(organizationId: string, page?: number): Promise<TMember[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZString], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const membersData = await prisma.membership.findMany({
|
||||
where: { organizationId },
|
||||
select: {
|
||||
user: {
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
try {
|
||||
const membersData = await prisma.membership.findMany({
|
||||
where: { organizationId },
|
||||
select: {
|
||||
user: {
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
userId: true,
|
||||
accepted: true,
|
||||
role: true,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
const members = membersData.map((member) => {
|
||||
return {
|
||||
name: member.user?.name || "",
|
||||
email: member.user?.email || "",
|
||||
userId: member.userId,
|
||||
accepted: member.accepted,
|
||||
role: member.role,
|
||||
};
|
||||
});
|
||||
|
||||
return members;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw new UnknownError("Error while fetching members");
|
||||
}
|
||||
},
|
||||
[`getMembersByOrganizationId-${organizationId}-${page}`],
|
||||
{
|
||||
tags: [membershipCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const getMembershipByUserIdOrganizationId = reactCache(
|
||||
(userId: string, organizationId: string): Promise<TMembership | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([userId, ZString], [organizationId, ZString]);
|
||||
|
||||
try {
|
||||
const membership = await prisma.membership.findUnique({
|
||||
where: {
|
||||
userId_organizationId: {
|
||||
userId,
|
||||
organizationId,
|
||||
},
|
||||
},
|
||||
userId: true,
|
||||
accepted: true,
|
||||
role: true,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
});
|
||||
|
||||
const members = membersData.map((member) => {
|
||||
return {
|
||||
name: member.user?.name || "",
|
||||
email: member.user?.email || "",
|
||||
userId: member.userId,
|
||||
accepted: member.accepted,
|
||||
role: member.role,
|
||||
};
|
||||
});
|
||||
if (!membership) return null;
|
||||
|
||||
return members;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
return membership;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw new UnknownError("Error while fetching membership");
|
||||
}
|
||||
|
||||
throw new UnknownError("Error while fetching members");
|
||||
},
|
||||
[`getMembershipByUserIdOrganizationId-${userId}-${organizationId}`],
|
||||
{
|
||||
tags: [membershipCache.tag.byUserId(userId), membershipCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
},
|
||||
[`getMembersByOrganizationId-${organizationId}-${page}`],
|
||||
{
|
||||
tags: [membershipCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getMembershipByUserIdOrganizationId = async (
|
||||
userId: string,
|
||||
organizationId: string
|
||||
): Promise<TMembership | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([userId, ZString], [organizationId, ZString]);
|
||||
export const getMembershipsByUserId = reactCache(
|
||||
(userId: string, page?: number): Promise<TMembership[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([userId, ZString], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const membership = await prisma.membership.findUnique({
|
||||
where: {
|
||||
userId_organizationId: {
|
||||
try {
|
||||
const memberships = await prisma.membership.findMany({
|
||||
where: {
|
||||
userId,
|
||||
organizationId,
|
||||
},
|
||||
},
|
||||
});
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
if (!membership) return null;
|
||||
return memberships;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
return membership;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new UnknownError("Error while fetching membership");
|
||||
},
|
||||
[`getMembershipsByUserId-${userId}-${page}`],
|
||||
{
|
||||
tags: [membershipCache.tag.byUserId(userId)],
|
||||
}
|
||||
},
|
||||
[`getMembershipByUserIdOrganizationId-${userId}-${organizationId}`],
|
||||
{
|
||||
tags: [membershipCache.tag.byUserId(userId), membershipCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getMembershipsByUserId = async (userId: string, page?: number): Promise<TMembership[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([userId, ZString], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const memberships = await prisma.membership.findMany({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
return memberships;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getMembershipsByUserId-${userId}-${page}`],
|
||||
{
|
||||
tags: [membershipCache.tag.byUserId(userId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const createMembership = async (
|
||||
organizationId: string,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
@@ -32,105 +33,111 @@ export const getOrganizationsByUserIdCacheTag = (userId: string) => `users-${use
|
||||
export const getOrganizationByEnvironmentIdCacheTag = (environmentId: string) =>
|
||||
`environments-${environmentId}-organization`;
|
||||
|
||||
export const getOrganizationsByUserId = (userId: string, page?: number): Promise<TOrganization[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([userId, ZString], [page, ZOptionalNumber]);
|
||||
export const getOrganizationsByUserId = reactCache(
|
||||
(userId: string, page?: number): Promise<TOrganization[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([userId, ZString], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const organizations = await prisma.organization.findMany({
|
||||
where: {
|
||||
memberships: {
|
||||
some: {
|
||||
userId,
|
||||
try {
|
||||
const organizations = await prisma.organization.findMany({
|
||||
where: {
|
||||
memberships: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
if (!organizations) {
|
||||
throw new ResourceNotFoundError("Organizations by UserId", userId);
|
||||
}
|
||||
return organizations;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
select,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
if (!organizations) {
|
||||
throw new ResourceNotFoundError("Organizations by UserId", userId);
|
||||
}
|
||||
return organizations;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getOrganizationsByUserId-${userId}-${page}`],
|
||||
{
|
||||
tags: [organizationCache.tag.byUserId(userId)],
|
||||
}
|
||||
},
|
||||
[`getOrganizationsByUserId-${userId}-${page}`],
|
||||
{
|
||||
tags: [organizationCache.tag.byUserId(userId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getOrganizationByEnvironmentId = (environmentId: string): Promise<TOrganization | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getOrganizationByEnvironmentId = reactCache(
|
||||
(environmentId: string): Promise<TOrganization | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const organization = await prisma.organization.findFirst({
|
||||
where: {
|
||||
products: {
|
||||
some: {
|
||||
environments: {
|
||||
some: {
|
||||
id: environmentId,
|
||||
try {
|
||||
const organization = await prisma.organization.findFirst({
|
||||
where: {
|
||||
products: {
|
||||
some: {
|
||||
environments: {
|
||||
some: {
|
||||
id: environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: { ...select, memberships: true }, // include memberships
|
||||
});
|
||||
select: { ...select, memberships: true }, // include memberships
|
||||
});
|
||||
|
||||
return organization;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
return organization;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getOrganizationByEnvironmentId-${environmentId}`],
|
||||
{
|
||||
tags: [organizationCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getOrganizationByEnvironmentId-${environmentId}`],
|
||||
{
|
||||
tags: [organizationCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getOrganization = (organizationId: string): Promise<TOrganization | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZString]);
|
||||
export const getOrganization = reactCache(
|
||||
(organizationId: string): Promise<TOrganization | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZString]);
|
||||
|
||||
try {
|
||||
const organization = await prisma.organization.findUnique({
|
||||
where: {
|
||||
id: organizationId,
|
||||
},
|
||||
select,
|
||||
});
|
||||
return organization;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
const organization = await prisma.organization.findUnique({
|
||||
where: {
|
||||
id: organizationId,
|
||||
},
|
||||
select,
|
||||
});
|
||||
return organization;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getOrganization-${organizationId}`],
|
||||
{
|
||||
tags: [organizationCache.tag.byId(organizationId)],
|
||||
}
|
||||
},
|
||||
[`getOrganization-${organizationId}`],
|
||||
{
|
||||
tags: [organizationCache.tag.byId(organizationId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const createOrganization = async (
|
||||
organizationInput: TOrganizationCreateInput
|
||||
@@ -271,79 +278,83 @@ export const deleteOrganization = async (organizationId: string): Promise<TOrgan
|
||||
}
|
||||
};
|
||||
|
||||
export const getMonthlyActiveOrganizationPeopleCount = (organizationId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZId]);
|
||||
export const getMonthlyActiveOrganizationPeopleCount = reactCache(
|
||||
(organizationId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZId]);
|
||||
|
||||
try {
|
||||
// temporary solution until we have a better way to track active users
|
||||
return 0;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
// temporary solution until we have a better way to track active users
|
||||
return 0;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getMonthlyActiveOrganizationPeopleCount-${organizationId}`],
|
||||
{
|
||||
revalidate: 60 * 60 * 2, // 2 hours
|
||||
}
|
||||
},
|
||||
[`getMonthlyActiveOrganizationPeopleCount-${organizationId}`],
|
||||
{
|
||||
revalidate: 60 * 60 * 2, // 2 hours
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getMonthlyOrganizationResponseCount = (organizationId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZId]);
|
||||
export const getMonthlyOrganizationResponseCount = reactCache(
|
||||
(organizationId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZId]);
|
||||
|
||||
try {
|
||||
// Define the start of the month
|
||||
// const now = new Date();
|
||||
// const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
try {
|
||||
// Define the start of the month
|
||||
// const now = new Date();
|
||||
// const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
|
||||
const organization = await getOrganization(organizationId);
|
||||
if (!organization) {
|
||||
throw new ResourceNotFoundError("Organization", organizationId);
|
||||
const organization = await getOrganization(organizationId);
|
||||
if (!organization) {
|
||||
throw new ResourceNotFoundError("Organization", organizationId);
|
||||
}
|
||||
|
||||
if (!organization.billing.periodStart) {
|
||||
throw new Error("Organization billing period start is not set");
|
||||
}
|
||||
|
||||
// Get all environment IDs for the organization
|
||||
const products = await getProducts(organizationId);
|
||||
const environmentIds = products.flatMap((product) => product.environments.map((env) => env.id));
|
||||
|
||||
// Use Prisma's aggregate to count responses for all environments
|
||||
const responseAggregations = await prisma.response.aggregate({
|
||||
_count: {
|
||||
id: true,
|
||||
},
|
||||
where: {
|
||||
AND: [
|
||||
{ survey: { environmentId: { in: environmentIds } } },
|
||||
{ createdAt: { gte: organization.billing.periodStart } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// The result is an aggregation of the total count
|
||||
return responseAggregations._count.id;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!organization.billing.periodStart) {
|
||||
throw new Error("Organization billing period start is not set");
|
||||
}
|
||||
|
||||
// Get all environment IDs for the organization
|
||||
const products = await getProducts(organizationId);
|
||||
const environmentIds = products.flatMap((product) => product.environments.map((env) => env.id));
|
||||
|
||||
// Use Prisma's aggregate to count responses for all environments
|
||||
const responseAggregations = await prisma.response.aggregate({
|
||||
_count: {
|
||||
id: true,
|
||||
},
|
||||
where: {
|
||||
AND: [
|
||||
{ survey: { environmentId: { in: environmentIds } } },
|
||||
{ createdAt: { gte: organization.billing.periodStart } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// The result is an aggregation of the total count
|
||||
return responseAggregations._count.id;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getMonthlyOrganizationResponseCount-${organizationId}`],
|
||||
{
|
||||
revalidate: 60 * 60 * 2, // 2 hours
|
||||
}
|
||||
},
|
||||
[`getMonthlyOrganizationResponseCount-${organizationId}`],
|
||||
{
|
||||
revalidate: 60 * 60 * 2, // 2 hours
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const subscribeOrganizationMembersToSurveyResponses = async (
|
||||
surveyId: string,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
@@ -51,84 +52,90 @@ export const transformPrismaPerson = (person: TransformPersonInput): TPerson =>
|
||||
} as TPerson;
|
||||
};
|
||||
|
||||
export const getPerson = (personId: string): Promise<TPerson | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId]);
|
||||
export const getPerson = reactCache(
|
||||
(personId: string): Promise<TPerson | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId]);
|
||||
|
||||
try {
|
||||
return await prisma.person.findUnique({
|
||||
where: {
|
||||
id: personId,
|
||||
},
|
||||
select: selectPerson,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
return await prisma.person.findUnique({
|
||||
where: {
|
||||
id: personId,
|
||||
},
|
||||
select: selectPerson,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getPerson-${personId}`],
|
||||
{
|
||||
tags: [personCache.tag.byId(personId)],
|
||||
}
|
||||
},
|
||||
[`getPerson-${personId}`],
|
||||
{
|
||||
tags: [personCache.tag.byId(personId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getPeople = (environmentId: string, page?: number): Promise<TPerson[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
export const getPeople = reactCache(
|
||||
(environmentId: string, page?: number): Promise<TPerson[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
return await prisma.person.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
select: selectPerson,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
return await prisma.person.findMany({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
select: selectPerson,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getPeople-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [personCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getPeople-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [personCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getPeopleCount = (environmentId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getPeopleCount = reactCache(
|
||||
(environmentId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
return await prisma.person.count({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
return await prisma.person.count({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getPeopleCount-${environmentId}`],
|
||||
{
|
||||
tags: [personCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getPeopleCount-${environmentId}`],
|
||||
{
|
||||
tags: [personCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const createPerson = async (environmentId: string, userId: string): Promise<TPerson> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
@@ -205,62 +212,66 @@ export const deletePerson = async (personId: string): Promise<TPerson | null> =>
|
||||
}
|
||||
};
|
||||
|
||||
export const getPersonByUserId = (environmentId: string, userId: string): Promise<TPerson | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [userId, ZString]);
|
||||
export const getPersonByUserId = reactCache(
|
||||
(environmentId: string, userId: string): Promise<TPerson | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [userId, ZString]);
|
||||
|
||||
// check if userId exists as a column
|
||||
const personWithUserId = await prisma.person.findFirst({
|
||||
where: {
|
||||
environmentId,
|
||||
userId,
|
||||
},
|
||||
select: selectPerson,
|
||||
});
|
||||
|
||||
if (personWithUserId) {
|
||||
return personWithUserId;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
[`getPersonByUserId-${environmentId}-${userId}`],
|
||||
{
|
||||
tags: [personCache.tag.byEnvironmentIdAndUserId(environmentId, userId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getIsPersonMonthlyActive = (personId: string): Promise<boolean> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const latestAction = await prisma.action.findFirst({
|
||||
// check if userId exists as a column
|
||||
const personWithUserId = await prisma.person.findFirst({
|
||||
where: {
|
||||
personId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
environmentId,
|
||||
userId,
|
||||
},
|
||||
select: selectPerson,
|
||||
});
|
||||
if (!latestAction || new Date(latestAction.createdAt).getMonth() !== new Date().getMonth()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
|
||||
if (personWithUserId) {
|
||||
return personWithUserId;
|
||||
}
|
||||
|
||||
throw error;
|
||||
return null;
|
||||
},
|
||||
[`getPersonByUserId-${environmentId}-${userId}`],
|
||||
{
|
||||
tags: [personCache.tag.byEnvironmentIdAndUserId(environmentId, userId)],
|
||||
}
|
||||
},
|
||||
[`getIsPersonMonthlyActive-${personId}`],
|
||||
{
|
||||
tags: [activePersonCache.tag.byId(personId)],
|
||||
revalidate: 60 * 60 * 24, // 24 hours
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getIsPersonMonthlyActive = reactCache(
|
||||
(personId: string): Promise<boolean> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const latestAction = await prisma.action.findFirst({
|
||||
where: {
|
||||
personId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
if (!latestAction || new Date(latestAction.createdAt).getMonth() !== new Date().getMonth()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getIsPersonMonthlyActive-${personId}`],
|
||||
{
|
||||
tags: [activePersonCache.tag.byId(personId)],
|
||||
revalidate: 60 * 60 * 24, // 24 hours
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
@@ -34,68 +35,72 @@ const selectProduct = {
|
||||
logo: true,
|
||||
};
|
||||
|
||||
export const getProducts = (organizationId: string, page?: number): Promise<TProduct[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZId], [page, ZOptionalNumber]);
|
||||
export const getProducts = reactCache(
|
||||
(organizationId: string, page?: number): Promise<TProduct[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([organizationId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const products = await prisma.product.findMany({
|
||||
where: {
|
||||
organizationId,
|
||||
},
|
||||
select: selectProduct,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
return products;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
const products = await prisma.product.findMany({
|
||||
where: {
|
||||
organizationId,
|
||||
},
|
||||
select: selectProduct,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
return products;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getProducts-${organizationId}-${page}`],
|
||||
{
|
||||
tags: [productCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
},
|
||||
[`getProducts-${organizationId}-${page}`],
|
||||
{
|
||||
tags: [productCache.tag.byOrganizationId(organizationId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getProductByEnvironmentId = (environmentId: string): Promise<TProduct | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getProductByEnvironmentId = reactCache(
|
||||
(environmentId: string): Promise<TProduct | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
let productPrisma;
|
||||
let productPrisma;
|
||||
|
||||
try {
|
||||
productPrisma = await prisma.product.findFirst({
|
||||
where: {
|
||||
environments: {
|
||||
some: {
|
||||
id: environmentId,
|
||||
try {
|
||||
productPrisma = await prisma.product.findFirst({
|
||||
where: {
|
||||
environments: {
|
||||
some: {
|
||||
id: environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: selectProduct,
|
||||
});
|
||||
select: selectProduct,
|
||||
});
|
||||
|
||||
return productPrisma;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
return productPrisma;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getProductByEnvironmentId-${environmentId}`],
|
||||
{
|
||||
tags: [productCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getProductByEnvironmentId-${environmentId}`],
|
||||
{
|
||||
tags: [productCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateProduct = async (
|
||||
productId: string,
|
||||
@@ -148,31 +153,33 @@ export const updateProduct = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getProduct = async (productId: string): Promise<TProduct | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
let productPrisma;
|
||||
try {
|
||||
productPrisma = await prisma.product.findUnique({
|
||||
where: {
|
||||
id: productId,
|
||||
},
|
||||
select: selectProduct,
|
||||
});
|
||||
export const getProduct = reactCache(
|
||||
(productId: string): Promise<TProduct | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
let productPrisma;
|
||||
try {
|
||||
productPrisma = await prisma.product.findUnique({
|
||||
where: {
|
||||
id: productId,
|
||||
},
|
||||
select: selectProduct,
|
||||
});
|
||||
|
||||
return productPrisma;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return productPrisma;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getProduct-${productId}`],
|
||||
{
|
||||
tags: [productCache.tag.byId(productId)],
|
||||
}
|
||||
},
|
||||
[`getProduct-${productId}`],
|
||||
{
|
||||
tags: [productCache.tag.byId(productId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const deleteProduct = async (productId: string): Promise<TProduct> => {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TAttributes } from "@formbricks/types/attributes";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
@@ -100,92 +101,96 @@ export const responseSelection = {
|
||||
},
|
||||
};
|
||||
|
||||
export const getResponsesByPersonId = (personId: string, page?: number): Promise<TResponse[] | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
export const getResponsesByPersonId = reactCache(
|
||||
(personId: string, page?: number): Promise<TResponse[] | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
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,
|
||||
orderBy: {
|
||||
updatedAt: "asc",
|
||||
},
|
||||
});
|
||||
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,
|
||||
orderBy: {
|
||||
updatedAt: "asc",
|
||||
},
|
||||
});
|
||||
|
||||
if (!responsePrisma) {
|
||||
throw new ResourceNotFoundError("Response from PersonId", personId);
|
||||
if (!responsePrisma) {
|
||||
throw new ResourceNotFoundError("Response from PersonId", personId);
|
||||
}
|
||||
|
||||
let responses: TResponse[] = [];
|
||||
|
||||
await Promise.all(
|
||||
responsePrisma.map(async (response) => {
|
||||
const responseNotes = await getResponseNotes(response.id);
|
||||
responses.push({
|
||||
...response,
|
||||
notes: responseNotes,
|
||||
tags: response.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
return responses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
let responses: TResponse[] = [];
|
||||
|
||||
await Promise.all(
|
||||
responsePrisma.map(async (response) => {
|
||||
const responseNotes = await getResponseNotes(response.id);
|
||||
responses.push({
|
||||
...response,
|
||||
notes: responseNotes,
|
||||
tags: response.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
return responses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getResponsesByPersonId-${personId}-${page}`],
|
||||
{
|
||||
tags: [responseCache.tag.byPersonId(personId)],
|
||||
}
|
||||
},
|
||||
[`getResponsesByPersonId-${personId}-${page}`],
|
||||
{
|
||||
tags: [responseCache.tag.byPersonId(personId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getResponseBySingleUseId = (surveyId: string, singleUseId: string): Promise<TResponse | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId], [singleUseId, ZString]);
|
||||
export const getResponseBySingleUseId = reactCache(
|
||||
(surveyId: string, singleUseId: string): Promise<TResponse | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId], [singleUseId, ZString]);
|
||||
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
surveyId_singleUseId: { surveyId, singleUseId },
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
surveyId_singleUseId: { surveyId, singleUseId },
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
|
||||
if (!responsePrisma) {
|
||||
return null;
|
||||
if (!responsePrisma) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const response: TResponse = {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const response: TResponse = {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getResponseBySingleUseId-${surveyId}-${singleUseId}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySingleUseId(surveyId, singleUseId)],
|
||||
}
|
||||
},
|
||||
[`getResponseBySingleUseId-${surveyId}-${singleUseId}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySingleUseId(surveyId, singleUseId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const createResponse = async (responseInput: TResponseInput): Promise<TResponse> => {
|
||||
validateInputs([responseInput, ZResponseInput]);
|
||||
@@ -378,44 +383,46 @@ export const createResponseLegacy = async (responseInput: TResponseLegacyInput):
|
||||
}
|
||||
};
|
||||
|
||||
export const getResponse = (responseId: string): Promise<TResponse | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([responseId, ZId]);
|
||||
export const getResponse = reactCache(
|
||||
(responseId: string): Promise<TResponse | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([responseId, ZId]);
|
||||
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
id: responseId,
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
id: responseId,
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
|
||||
if (!responsePrisma) {
|
||||
return null;
|
||||
if (!responsePrisma) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const response: TResponse = {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const response: TResponse = {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getResponse-${responseId}`],
|
||||
{
|
||||
tags: [responseCache.tag.byId(responseId), responseNoteCache.tag.byResponseId(responseId)],
|
||||
}
|
||||
},
|
||||
[`getResponse-${responseId}`],
|
||||
{
|
||||
tags: [responseCache.tag.byId(responseId), responseNoteCache.tag.byResponseId(responseId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getResponseFilteringValues = async (surveyId: string) =>
|
||||
export const getResponseFilteringValues = reactCache((surveyId: string) =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
@@ -454,111 +461,113 @@ export const getResponseFilteringValues = async (surveyId: string) =>
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getResponses = (
|
||||
surveyId: string,
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
filterCriteria?: TResponseFilterCriteria
|
||||
): Promise<TResponse[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs(
|
||||
[surveyId, ZId],
|
||||
[limit, ZOptionalNumber],
|
||||
[offset, ZOptionalNumber],
|
||||
[filterCriteria, ZResponseFilterCriteria.optional()]
|
||||
);
|
||||
export const getResponses = reactCache(
|
||||
(
|
||||
surveyId: string,
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
filterCriteria?: TResponseFilterCriteria
|
||||
): Promise<TResponse[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs(
|
||||
[surveyId, ZId],
|
||||
[limit, ZOptionalNumber],
|
||||
[offset, ZOptionalNumber],
|
||||
[filterCriteria, ZResponseFilterCriteria.optional()]
|
||||
);
|
||||
|
||||
limit = limit ?? RESPONSES_PER_PAGE;
|
||||
try {
|
||||
const responses = await prisma.response.findMany({
|
||||
where: {
|
||||
surveyId,
|
||||
...buildWhereClause(filterCriteria),
|
||||
},
|
||||
select: responseSelection,
|
||||
orderBy: [
|
||||
{
|
||||
createdAt: "desc",
|
||||
limit = limit ?? RESPONSES_PER_PAGE;
|
||||
try {
|
||||
const responses = await prisma.response.findMany({
|
||||
where: {
|
||||
surveyId,
|
||||
...buildWhereClause(filterCriteria),
|
||||
},
|
||||
],
|
||||
take: limit ? limit : undefined,
|
||||
skip: offset ? offset : undefined,
|
||||
});
|
||||
select: responseSelection,
|
||||
orderBy: [
|
||||
{
|
||||
createdAt: "desc",
|
||||
},
|
||||
],
|
||||
take: limit ? limit : undefined,
|
||||
skip: offset ? offset : undefined,
|
||||
});
|
||||
|
||||
const transformedResponses: TResponse[] = await Promise.all(
|
||||
responses.map((responsePrisma) => {
|
||||
return {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
})
|
||||
);
|
||||
const transformedResponses: TResponse[] = await Promise.all(
|
||||
responses.map((responsePrisma) => {
|
||||
return {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return transformedResponses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return transformedResponses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getResponses-${surveyId}-${limit}-${offset}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
},
|
||||
[`getResponses-${surveyId}-${limit}-${offset}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getSurveySummary = (
|
||||
surveyId: string,
|
||||
filterCriteria?: TResponseFilterCriteria
|
||||
): Promise<TSurveySummary> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId], [filterCriteria, ZResponseFilterCriteria.optional()]);
|
||||
export const getSurveySummary = reactCache(
|
||||
(surveyId: string, filterCriteria?: TResponseFilterCriteria): Promise<TSurveySummary> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId], [filterCriteria, ZResponseFilterCriteria.optional()]);
|
||||
|
||||
try {
|
||||
const survey = await getSurvey(surveyId);
|
||||
if (!survey) {
|
||||
throw new ResourceNotFoundError("Survey", surveyId);
|
||||
try {
|
||||
const survey = await getSurvey(surveyId);
|
||||
if (!survey) {
|
||||
throw new ResourceNotFoundError("Survey", surveyId);
|
||||
}
|
||||
|
||||
const batchSize = 3000;
|
||||
const responseCount = await getResponseCountBySurveyId(surveyId, filterCriteria);
|
||||
const pages = Math.ceil(responseCount / batchSize);
|
||||
|
||||
const responsesArray = await Promise.all(
|
||||
Array.from({ length: pages }, (_, i) => {
|
||||
return getResponses(surveyId, batchSize, i * batchSize, filterCriteria);
|
||||
})
|
||||
);
|
||||
const responses = responsesArray.flat();
|
||||
|
||||
const displayCount = await getDisplayCountBySurveyId(surveyId, {
|
||||
createdAt: filterCriteria?.createdAt,
|
||||
});
|
||||
|
||||
const dropOff = getSurveySummaryDropOff(survey, responses, displayCount);
|
||||
const meta = getSurveySummaryMeta(responses, displayCount);
|
||||
const questionWiseSummary = getQuestionWiseSummary(survey, responses, dropOff);
|
||||
|
||||
return { meta, dropOff, summary: questionWiseSummary };
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const batchSize = 3000;
|
||||
const responseCount = await getResponseCountBySurveyId(surveyId, filterCriteria);
|
||||
const pages = Math.ceil(responseCount / batchSize);
|
||||
|
||||
const responsesArray = await Promise.all(
|
||||
Array.from({ length: pages }, (_, i) => {
|
||||
return getResponses(surveyId, batchSize, i * batchSize, filterCriteria);
|
||||
})
|
||||
);
|
||||
const responses = responsesArray.flat();
|
||||
|
||||
const displayCount = await getDisplayCountBySurveyId(surveyId, {
|
||||
createdAt: filterCriteria?.createdAt,
|
||||
});
|
||||
|
||||
const dropOff = getSurveySummaryDropOff(survey, responses, displayCount);
|
||||
const meta = getSurveySummaryMeta(responses, displayCount);
|
||||
const questionWiseSummary = getQuestionWiseSummary(survey, responses, dropOff);
|
||||
|
||||
return { meta, dropOff, summary: questionWiseSummary };
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getSurveySummary-${surveyId}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId), displayCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
},
|
||||
[`getSurveySummary-${surveyId}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId), displayCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getResponseDownloadUrl = async (
|
||||
surveyId: string,
|
||||
@@ -632,55 +641,53 @@ export const getResponseDownloadUrl = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getResponsesByEnvironmentId = (
|
||||
environmentId: string,
|
||||
limit?: number,
|
||||
offset?: number
|
||||
): Promise<TResponse[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [limit, ZOptionalNumber], [offset, ZOptionalNumber]);
|
||||
export const getResponsesByEnvironmentId = reactCache(
|
||||
(environmentId: string, limit?: number, offset?: number): Promise<TResponse[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [limit, ZOptionalNumber], [offset, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const responses = await prisma.response.findMany({
|
||||
where: {
|
||||
survey: {
|
||||
environmentId,
|
||||
try {
|
||||
const responses = await prisma.response.findMany({
|
||||
where: {
|
||||
survey: {
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: responseSelection,
|
||||
orderBy: [
|
||||
{
|
||||
createdAt: "desc",
|
||||
},
|
||||
],
|
||||
take: limit ? limit : undefined,
|
||||
skip: offset ? offset : undefined,
|
||||
});
|
||||
select: responseSelection,
|
||||
orderBy: [
|
||||
{
|
||||
createdAt: "desc",
|
||||
},
|
||||
],
|
||||
take: limit ? limit : undefined,
|
||||
skip: offset ? offset : undefined,
|
||||
});
|
||||
|
||||
const transformedResponses: TResponse[] = await Promise.all(
|
||||
responses.map(async (responsePrisma) => {
|
||||
return {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
})
|
||||
);
|
||||
const transformedResponses: TResponse[] = await Promise.all(
|
||||
responses.map(async (responsePrisma) => {
|
||||
return {
|
||||
...responsePrisma,
|
||||
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return transformedResponses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return transformedResponses;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getResponsesByEnvironmentId-${environmentId}-${limit}-${offset}`],
|
||||
{
|
||||
tags: [responseCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getResponsesByEnvironmentId-${environmentId}-${limit}-${offset}`],
|
||||
{
|
||||
tags: [responseCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateResponse = async (
|
||||
responseId: string,
|
||||
@@ -794,32 +801,31 @@ export const deleteResponse = async (responseId: string): Promise<TResponse> =>
|
||||
}
|
||||
};
|
||||
|
||||
export const getResponseCountBySurveyId = (
|
||||
surveyId: string,
|
||||
filterCriteria?: TResponseFilterCriteria
|
||||
): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId], [filterCriteria, ZResponseFilterCriteria.optional()]);
|
||||
export const getResponseCountBySurveyId = reactCache(
|
||||
(surveyId: string, filterCriteria?: TResponseFilterCriteria): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId], [filterCriteria, ZResponseFilterCriteria.optional()]);
|
||||
|
||||
try {
|
||||
const responseCount = await prisma.response.count({
|
||||
where: {
|
||||
surveyId: surveyId,
|
||||
...buildWhereClause(filterCriteria),
|
||||
},
|
||||
});
|
||||
return responseCount;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
try {
|
||||
const responseCount = await prisma.response.count({
|
||||
where: {
|
||||
surveyId: surveyId,
|
||||
...buildWhereClause(filterCriteria),
|
||||
},
|
||||
});
|
||||
return responseCount;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getResponseCountBySurveyId-${surveyId}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
},
|
||||
[`getResponseCountBySurveyId-${surveyId}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
@@ -68,62 +69,66 @@ export const createResponseNote = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const getResponseNote = (responseNoteId: string): Promise<TResponseNote | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const responseNote = await prisma.responseNote.findUnique({
|
||||
where: {
|
||||
id: responseNoteId,
|
||||
},
|
||||
select: responseNoteSelect,
|
||||
});
|
||||
return responseNote;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
export const getResponseNote = reactCache(
|
||||
(responseNoteId: string): Promise<TResponseNote | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const responseNote = await prisma.responseNote.findUnique({
|
||||
where: {
|
||||
id: responseNoteId,
|
||||
},
|
||||
select: responseNoteSelect,
|
||||
});
|
||||
return responseNote;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[`getResponseNote-${responseNoteId}`],
|
||||
{
|
||||
tags: [responseNoteCache.tag.byId(responseNoteId)],
|
||||
}
|
||||
},
|
||||
[`getResponseNote-${responseNoteId}`],
|
||||
{
|
||||
tags: [responseNoteCache.tag.byId(responseNoteId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getResponseNotes = (responseId: string): Promise<TResponseNote[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
validateInputs([responseId, ZId]);
|
||||
export const getResponseNotes = reactCache(
|
||||
(responseId: string): Promise<TResponseNote[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
validateInputs([responseId, ZId]);
|
||||
|
||||
const responseNotes = await prisma.responseNote.findMany({
|
||||
where: {
|
||||
responseId,
|
||||
},
|
||||
select: responseNoteSelect,
|
||||
});
|
||||
if (!responseNotes) {
|
||||
throw new ResourceNotFoundError("Response Notes by ResponseId", responseId);
|
||||
const responseNotes = await prisma.responseNote.findMany({
|
||||
where: {
|
||||
responseId,
|
||||
},
|
||||
select: responseNoteSelect,
|
||||
});
|
||||
if (!responseNotes) {
|
||||
throw new ResourceNotFoundError("Response Notes by ResponseId", responseId);
|
||||
}
|
||||
return responseNotes;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
return responseNotes;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getResponseNotes-${responseId}`],
|
||||
{
|
||||
tags: [responseNoteCache.tag.byResponseId(responseId)],
|
||||
}
|
||||
},
|
||||
[`getResponseNotes-${responseId}`],
|
||||
{
|
||||
tags: [responseNoteCache.tag.byResponseId(responseId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateResponseNote = async (responseNoteId: string, text: string): Promise<TResponseNote> => {
|
||||
validateInputs([responseNoteId, ZString], [text, ZString]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
@@ -116,67 +117,71 @@ export const createSegment = async (segmentCreateInput: TSegmentCreateInput): Pr
|
||||
}
|
||||
};
|
||||
|
||||
export const getSegments = (environmentId: string): Promise<TSegment[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const segments = await prisma.segment.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
select: selectSegment,
|
||||
});
|
||||
export const getSegments = reactCache(
|
||||
(environmentId: string): Promise<TSegment[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const segments = await prisma.segment.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
select: selectSegment,
|
||||
});
|
||||
|
||||
if (!segments) {
|
||||
return [];
|
||||
if (!segments) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return segments.map((segment) => transformPrismaSegment(segment));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return segments.map((segment) => transformPrismaSegment(segment));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getSegments-${environmentId}`],
|
||||
{
|
||||
tags: [segmentCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getSegments-${environmentId}`],
|
||||
{
|
||||
tags: [segmentCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getSegment = (segmentId: string): Promise<TSegment> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([segmentId, ZId]);
|
||||
try {
|
||||
const segment = await prisma.segment.findUnique({
|
||||
where: {
|
||||
id: segmentId,
|
||||
},
|
||||
select: selectSegment,
|
||||
});
|
||||
export const getSegment = reactCache(
|
||||
(segmentId: string): Promise<TSegment> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([segmentId, ZId]);
|
||||
try {
|
||||
const segment = await prisma.segment.findUnique({
|
||||
where: {
|
||||
id: segmentId,
|
||||
},
|
||||
select: selectSegment,
|
||||
});
|
||||
|
||||
if (!segment) {
|
||||
throw new ResourceNotFoundError("segment", segmentId);
|
||||
if (!segment) {
|
||||
throw new ResourceNotFoundError("segment", segmentId);
|
||||
}
|
||||
|
||||
return transformPrismaSegment(segment);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return transformPrismaSegment(segment);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getSegment-${segmentId}`],
|
||||
{
|
||||
tags: [segmentCache.tag.byId(segmentId)],
|
||||
}
|
||||
},
|
||||
[`getSegment-${segmentId}`],
|
||||
{
|
||||
tags: [segmentCache.tag.byId(segmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateSegment = async (segmentId: string, data: TSegmentUpdateInput): Promise<TSegment> => {
|
||||
validateInputs([segmentId, ZId], [data, ZSegmentUpdateInput]);
|
||||
@@ -330,43 +335,45 @@ export const cloneSegment = async (segmentId: string, surveyId: string): Promise
|
||||
}
|
||||
};
|
||||
|
||||
export const getSegmentsByAttributeClassName = (environmentId: string, attributeClassName: string) =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [attributeClassName, ZString]);
|
||||
export const getSegmentsByAttributeClassName = reactCache(
|
||||
(environmentId: string, attributeClassName: string) =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [attributeClassName, ZString]);
|
||||
|
||||
try {
|
||||
const segments = await prisma.segment.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
select: selectSegment,
|
||||
});
|
||||
try {
|
||||
const segments = await prisma.segment.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
select: selectSegment,
|
||||
});
|
||||
|
||||
// search for attributeClassName in the filters
|
||||
const clonedSegments = structuredClone(segments);
|
||||
// search for attributeClassName in the filters
|
||||
const clonedSegments = structuredClone(segments);
|
||||
|
||||
const filteredSegments = clonedSegments.filter((segment) => {
|
||||
return searchForAttributeClassNameInSegment(segment.filters, attributeClassName);
|
||||
});
|
||||
const filteredSegments = clonedSegments.filter((segment) => {
|
||||
return searchForAttributeClassNameInSegment(segment.filters, attributeClassName);
|
||||
});
|
||||
|
||||
return filteredSegments;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return filteredSegments;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getSegmentsByAttributeClassName-${environmentId}-${attributeClassName}`],
|
||||
{
|
||||
tags: [
|
||||
segmentCache.tag.byEnvironmentId(environmentId),
|
||||
segmentCache.tag.byAttributeClassName(attributeClassName),
|
||||
],
|
||||
}
|
||||
},
|
||||
[`getSegmentsByAttributeClassName-${environmentId}-${attributeClassName}`],
|
||||
{
|
||||
tags: [
|
||||
segmentCache.tag.byEnvironmentId(environmentId),
|
||||
segmentCache.tag.byAttributeClassName(attributeClassName),
|
||||
],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const resetSegmentInSurvey = async (surveyId: string): Promise<TSegment> => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
|
||||
26
packages/lib/shortUrl/cache.ts
Normal file
26
packages/lib/shortUrl/cache.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { revalidateTag } from "next/cache";
|
||||
|
||||
interface RevalidateProps {
|
||||
id?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export const shortUrlCache = {
|
||||
tag: {
|
||||
byId(id: string) {
|
||||
return `shortUrls-${id}`;
|
||||
},
|
||||
byUrl(url: string) {
|
||||
return `shortUrls-byUrl-${url}`;
|
||||
},
|
||||
},
|
||||
revalidate({ id, url }: RevalidateProps): void {
|
||||
if (id) {
|
||||
revalidateTag(this.tag.byId(id));
|
||||
}
|
||||
|
||||
if (url) {
|
||||
revalidateTag(this.tag.byUrl(url));
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import z from "zod";
|
||||
import { cache as reactCache } from "react";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { TShortUrl, ZShortUrlId } from "@formbricks/types/shortUrl";
|
||||
import { cache } from "../cache";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { shortUrlCache } from "./cache";
|
||||
|
||||
// Create the short url and return it
|
||||
export const createShortUrl = async (url: string): Promise<TShortUrl> => {
|
||||
@@ -21,12 +24,19 @@ export const createShortUrl = async (url: string): Promise<TShortUrl> => {
|
||||
// If an entry with the provided fullUrl does not exist, create a new one.
|
||||
const id = customAlphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 10)();
|
||||
|
||||
return await prisma.shortUrl.create({
|
||||
const shortUrl = await prisma.shortUrl.create({
|
||||
data: {
|
||||
id,
|
||||
url,
|
||||
},
|
||||
});
|
||||
|
||||
shortUrlCache.revalidate({
|
||||
id: shortUrl.id,
|
||||
url: shortUrl.url,
|
||||
});
|
||||
|
||||
return shortUrl;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
@@ -37,36 +47,54 @@ export const createShortUrl = async (url: string): Promise<TShortUrl> => {
|
||||
};
|
||||
|
||||
// Get the full url from short url and return it
|
||||
export const getShortUrl = async (id: string): Promise<TShortUrl | null> => {
|
||||
validateInputs([id, ZShortUrlId]);
|
||||
try {
|
||||
return await prisma.shortUrl.findUnique({
|
||||
where: {
|
||||
id,
|
||||
export const getShortUrl = reactCache(
|
||||
(id: string): Promise<TShortUrl | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([id, ZShortUrlId]);
|
||||
try {
|
||||
return await prisma.shortUrl.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
[`getShortUrl-${id}`],
|
||||
{
|
||||
tags: [shortUrlCache.tag.byId(id)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
export const getShortUrlByUrl = reactCache(
|
||||
(url: string): Promise<TShortUrl | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([url, z.string().url()]);
|
||||
try {
|
||||
return await prisma.shortUrl.findUnique({
|
||||
where: {
|
||||
url,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
export const getShortUrlByUrl = async (url: string): Promise<TShortUrl | null> => {
|
||||
validateInputs([url, z.string().url()]);
|
||||
try {
|
||||
return await prisma.shortUrl.findUnique({
|
||||
where: {
|
||||
url,
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
[`getShortUrlByUrl-${url}`],
|
||||
{
|
||||
tags: [shortUrlCache.tag.byUrl(url)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ interface RevalidateProps {
|
||||
actionClassId?: string;
|
||||
environmentId?: string;
|
||||
segmentId?: string;
|
||||
resultShareKey?: string;
|
||||
}
|
||||
|
||||
export const surveyCache = {
|
||||
@@ -25,8 +26,18 @@ export const surveyCache = {
|
||||
bySegmentId(segmentId: string) {
|
||||
return `segments-${segmentId}-surveys`;
|
||||
},
|
||||
byResultShareKey(resultShareKey: string) {
|
||||
return `surveys-resultShare-${resultShareKey}`;
|
||||
},
|
||||
},
|
||||
revalidate({ id, attributeClassId, actionClassId, environmentId, segmentId }: RevalidateProps): void {
|
||||
revalidate({
|
||||
id,
|
||||
attributeClassId,
|
||||
actionClassId,
|
||||
environmentId,
|
||||
segmentId,
|
||||
resultShareKey,
|
||||
}: RevalidateProps): void {
|
||||
if (id) {
|
||||
revalidateTag(this.tag.byId(id));
|
||||
}
|
||||
@@ -44,6 +55,10 @@ export const surveyCache = {
|
||||
}
|
||||
|
||||
if (segmentId) {
|
||||
revalidateTag(this.tag.byResultShareKey(resultShareKey));
|
||||
}
|
||||
|
||||
if (resultShareKey) {
|
||||
revalidateTag(this.tag.bySegmentId(segmentId));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TActionClass } from "@formbricks/types/actionClasses";
|
||||
import { ZOptionalNumber } from "@formbricks/types/common";
|
||||
@@ -182,128 +183,134 @@ const handleTriggerUpdates = (
|
||||
return triggersUpdate;
|
||||
};
|
||||
|
||||
export const getSurvey = (surveyId: string): Promise<TSurvey | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
export const getSurvey = reactCache(
|
||||
(surveyId: string): Promise<TSurvey | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
|
||||
let surveyPrisma;
|
||||
try {
|
||||
surveyPrisma = await prisma.survey.findUnique({
|
||||
where: {
|
||||
id: surveyId,
|
||||
},
|
||||
select: selectSurvey,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
let surveyPrisma;
|
||||
try {
|
||||
surveyPrisma = await prisma.survey.findUnique({
|
||||
where: {
|
||||
id: surveyId,
|
||||
},
|
||||
select: selectSurvey,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
|
||||
if (!surveyPrisma) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return transformPrismaSurvey(surveyPrisma);
|
||||
},
|
||||
[`getSurvey-${surveyId}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byId(surveyId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
if (!surveyPrisma) {
|
||||
return null;
|
||||
}
|
||||
export const getSurveysByActionClassId = reactCache(
|
||||
(actionClassId: string, page?: number): Promise<TSurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
return transformPrismaSurvey(surveyPrisma);
|
||||
},
|
||||
[`getSurvey-${surveyId}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byId(surveyId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getSurveysByActionClassId = (actionClassId: string, page?: number): Promise<TSurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([actionClassId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
let surveysPrisma;
|
||||
try {
|
||||
surveysPrisma = await prisma.survey.findMany({
|
||||
where: {
|
||||
triggers: {
|
||||
some: {
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
let surveysPrisma;
|
||||
try {
|
||||
surveysPrisma = await prisma.survey.findMany({
|
||||
where: {
|
||||
triggers: {
|
||||
some: {
|
||||
actionClass: {
|
||||
id: actionClassId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: selectSurvey,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
select: selectSurvey,
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
const surveys: TSurvey[] = [];
|
||||
|
||||
const surveys: TSurvey[] = [];
|
||||
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
}
|
||||
|
||||
return surveys;
|
||||
},
|
||||
[`getSurveysByActionClassId-${actionClassId}-${page}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byActionClassId(actionClassId)],
|
||||
}
|
||||
)();
|
||||
|
||||
export const getSurveys = (
|
||||
environmentId: string,
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
filterCriteria?: TSurveyFilterCriteria
|
||||
): Promise<TSurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [limit, ZOptionalNumber], [offset, ZOptionalNumber]);
|
||||
let surveysPrisma;
|
||||
|
||||
try {
|
||||
surveysPrisma = await prisma.survey.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
...buildWhereClause(filterCriteria),
|
||||
},
|
||||
select: selectSurvey,
|
||||
orderBy: buildOrderByClause(filterCriteria?.sortBy),
|
||||
take: limit ? limit : undefined,
|
||||
skip: offset ? offset : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
}
|
||||
|
||||
throw error;
|
||||
return surveys;
|
||||
},
|
||||
[`getSurveysByActionClassId-${actionClassId}-${page}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byActionClassId(actionClassId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
const surveys: TSurvey[] = [];
|
||||
export const getSurveys = reactCache(
|
||||
(
|
||||
environmentId: string,
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
filterCriteria?: TSurveyFilterCriteria
|
||||
): Promise<TSurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [limit, ZOptionalNumber], [offset, ZOptionalNumber]);
|
||||
let surveysPrisma;
|
||||
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
try {
|
||||
surveysPrisma = await prisma.survey.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
...buildWhereClause(filterCriteria),
|
||||
},
|
||||
select: selectSurvey,
|
||||
orderBy: buildOrderByClause(filterCriteria?.sortBy),
|
||||
take: limit ? limit : undefined,
|
||||
skip: offset ? offset : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const surveys: TSurvey[] = [];
|
||||
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
}
|
||||
return surveys;
|
||||
},
|
||||
[`getSurveys-${environmentId}-${limit}-${offset}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
return surveys;
|
||||
},
|
||||
[`getSurveys-${environmentId}-${limit}-${offset}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const transformToLegacySurvey = async (
|
||||
survey: TSurvey,
|
||||
@@ -322,32 +329,34 @@ export const transformToLegacySurvey = async (
|
||||
return transformedSurvey;
|
||||
};
|
||||
|
||||
export const getSurveyCount = async (environmentId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const surveyCount = await prisma.survey.count({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
});
|
||||
export const getSurveyCount = reactCache(
|
||||
(environmentId: string): Promise<number> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const surveyCount = await prisma.survey.count({
|
||||
where: {
|
||||
environmentId: environmentId,
|
||||
},
|
||||
});
|
||||
|
||||
return surveyCount;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
return surveyCount;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getSurveyCount-${environmentId}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getSurveyCount-${environmentId}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateSurvey = async (updatedSurvey: TSurvey): Promise<TSurvey> => {
|
||||
validateInputs([updatedSurvey, ZSurvey]);
|
||||
@@ -506,6 +515,7 @@ export const updateSurvey = async (updatedSurvey: TSurvey): Promise<TSurvey> =>
|
||||
id: modifiedSurvey.id,
|
||||
environmentId: modifiedSurvey.environmentId,
|
||||
segmentId: modifiedSurvey.segment?.id,
|
||||
resultShareKey: modifiedSurvey.resultShareKey ?? undefined,
|
||||
});
|
||||
|
||||
return modifiedSurvey;
|
||||
@@ -552,6 +562,7 @@ export const deleteSurvey = async (surveyId: string) => {
|
||||
surveyCache.revalidate({
|
||||
id: deletedSurvey.id,
|
||||
environmentId: deletedSurvey.environmentId,
|
||||
resultShareKey: deletedSurvey.resultShareKey ?? undefined,
|
||||
});
|
||||
|
||||
if (deletedSurvey.segment?.id) {
|
||||
@@ -665,6 +676,7 @@ export const createSurvey = async (environmentId: string, surveyBody: TSurveyInp
|
||||
surveyCache.revalidate({
|
||||
id: survey.id,
|
||||
environmentId: survey.environmentId,
|
||||
resultShareKey: survey.resultShareKey ?? undefined,
|
||||
});
|
||||
|
||||
if (createdBy) {
|
||||
@@ -796,6 +808,7 @@ export const duplicateSurvey = async (environmentId: string, surveyId: string, u
|
||||
surveyCache.revalidate({
|
||||
id: newSurvey.id,
|
||||
environmentId: newSurvey.environmentId,
|
||||
resultShareKey: newSurvey.resultShareKey ?? undefined,
|
||||
});
|
||||
|
||||
existingSurvey.triggers.forEach((trigger) => {
|
||||
@@ -815,226 +828,237 @@ export const duplicateSurvey = async (environmentId: string, surveyId: string, u
|
||||
}
|
||||
};
|
||||
|
||||
export const getSyncSurveys = (
|
||||
environmentId: string,
|
||||
personId: string,
|
||||
deviceType: "phone" | "desktop" = "desktop",
|
||||
options?: {
|
||||
version?: string;
|
||||
}
|
||||
): Promise<TSurvey[] | TLegacySurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const product = await getProductByEnvironmentId(environmentId);
|
||||
export const getSyncSurveys = reactCache(
|
||||
(
|
||||
environmentId: string,
|
||||
personId: string,
|
||||
deviceType: "phone" | "desktop" = "desktop",
|
||||
options?: {
|
||||
version?: string;
|
||||
}
|
||||
): Promise<TSurvey[] | TLegacySurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const product = await getProductByEnvironmentId(environmentId);
|
||||
|
||||
if (!product) {
|
||||
throw new Error("Product not found");
|
||||
}
|
||||
if (!product) {
|
||||
throw new Error("Product not found");
|
||||
}
|
||||
|
||||
const person = personId === "legacy" ? ({ id: "legacy" } as TPerson) : await getPerson(personId);
|
||||
const person = personId === "legacy" ? ({ id: "legacy" } as TPerson) : await getPerson(personId);
|
||||
|
||||
if (!person) {
|
||||
throw new Error("Person not found");
|
||||
}
|
||||
if (!person) {
|
||||
throw new Error("Person not found");
|
||||
}
|
||||
|
||||
let surveys: TSurvey[] | TLegacySurvey[] = await getSurveys(environmentId);
|
||||
let surveys: TSurvey[] | TLegacySurvey[] = await getSurveys(environmentId);
|
||||
|
||||
// filtered surveys for running and web
|
||||
surveys = surveys.filter((survey) => survey.status === "inProgress" && survey.type === "app");
|
||||
// filtered surveys for running and web
|
||||
surveys = surveys.filter((survey) => survey.status === "inProgress" && survey.type === "app");
|
||||
|
||||
// if no surveys are left, return an empty array
|
||||
if (surveys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
// if no surveys are left, return an empty array
|
||||
if (surveys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const displays = await getDisplaysByPersonId(person.id);
|
||||
const displays = await getDisplaysByPersonId(person.id);
|
||||
|
||||
// filter surveys that meet the displayOption criteria
|
||||
surveys = surveys.filter((survey) => {
|
||||
switch (survey.displayOption) {
|
||||
case "respondMultiple":
|
||||
// filter surveys that meet the displayOption criteria
|
||||
surveys = surveys.filter((survey) => {
|
||||
switch (survey.displayOption) {
|
||||
case "respondMultiple":
|
||||
return true;
|
||||
case "displayOnce":
|
||||
return displays.filter((display) => display.surveyId === survey.id).length === 0;
|
||||
case "displayMultiple":
|
||||
return (
|
||||
displays
|
||||
.filter((display) => display.surveyId === survey.id)
|
||||
.filter((display) => display.responseId).length === 0
|
||||
);
|
||||
case "displaySome":
|
||||
if (survey.displayLimit === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
displays
|
||||
.filter((display) => display.surveyId === survey.id)
|
||||
.some((display) => display.responseId)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
displays.filter((display) => display.surveyId === survey.id).length < survey.displayLimit
|
||||
);
|
||||
default:
|
||||
throw Error("Invalid displayOption");
|
||||
}
|
||||
});
|
||||
|
||||
const latestDisplay = displays[0];
|
||||
|
||||
// filter surveys that meet the recontactDays criteria
|
||||
surveys = surveys.filter((survey) => {
|
||||
if (!latestDisplay) {
|
||||
return true;
|
||||
case "displayOnce":
|
||||
return displays.filter((display) => display.surveyId === survey.id).length === 0;
|
||||
case "displayMultiple":
|
||||
return (
|
||||
displays
|
||||
.filter((display) => display.surveyId === survey.id)
|
||||
.filter((display) => display.responseId).length === 0
|
||||
);
|
||||
case "displaySome":
|
||||
if (survey.displayLimit === null) {
|
||||
} else if (survey.recontactDays !== null) {
|
||||
const lastDisplaySurvey = displays.filter((display) => display.surveyId === survey.id)[0];
|
||||
if (!lastDisplaySurvey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
displays
|
||||
.filter((display) => display.surveyId === survey.id)
|
||||
.some((display) => display.responseId)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
displays.filter((display) => display.surveyId === survey.id).length < survey.displayLimit
|
||||
);
|
||||
default:
|
||||
throw Error("Invalid displayOption");
|
||||
}
|
||||
});
|
||||
|
||||
const latestDisplay = displays[0];
|
||||
|
||||
// filter surveys that meet the recontactDays criteria
|
||||
surveys = surveys.filter((survey) => {
|
||||
if (!latestDisplay) {
|
||||
return true;
|
||||
} else if (survey.recontactDays !== null) {
|
||||
const lastDisplaySurvey = displays.filter((display) => display.surveyId === survey.id)[0];
|
||||
if (!lastDisplaySurvey) {
|
||||
return diffInDays(new Date(), new Date(lastDisplaySurvey.createdAt)) >= survey.recontactDays;
|
||||
} else if (product.recontactDays !== null) {
|
||||
return diffInDays(new Date(), new Date(latestDisplay.createdAt)) >= product.recontactDays;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return diffInDays(new Date(), new Date(lastDisplaySurvey.createdAt)) >= survey.recontactDays;
|
||||
} else if (product.recontactDays !== null) {
|
||||
return diffInDays(new Date(), new Date(latestDisplay.createdAt)) >= product.recontactDays;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// if no surveys are left, return an empty array
|
||||
if (surveys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// if no surveys have segment filters, return the surveys
|
||||
if (!anySurveyHasFilters(surveys)) {
|
||||
return surveys;
|
||||
}
|
||||
|
||||
const personActions = await getActionsByPersonId(person.id);
|
||||
const personActionClassIds = Array.from(
|
||||
new Set(personActions?.map((action) => action.actionClass?.id ?? ""))
|
||||
);
|
||||
|
||||
const attributes = await getAttributes(person.id);
|
||||
const personUserId = person.userId;
|
||||
|
||||
// the surveys now have segment filters, so we need to evaluate them
|
||||
const surveyPromises = surveys.map(async (survey) => {
|
||||
const { segment } = survey;
|
||||
// if the survey has no segment, or the segment has no filters, we return the survey
|
||||
if (!segment || !segment.filters?.length) {
|
||||
return survey;
|
||||
// if no surveys are left, return an empty array
|
||||
if (surveys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// backwards compatibility for older versions of the js package
|
||||
// if the version is not provided, we will use the old method of evaluating the segment, which is attribute filters
|
||||
// transform the segment filters to attribute filters and evaluate them
|
||||
if (!options?.version) {
|
||||
const attributeFilters = transformSegmentFiltersToAttributeFilters(segment.filters);
|
||||
// if no surveys have segment filters, return the surveys
|
||||
if (!anySurveyHasFilters(surveys)) {
|
||||
return surveys;
|
||||
}
|
||||
|
||||
// if the attribute filters are null, it means the segment filters don't match the expected format for attribute filters, so we skip this survey
|
||||
if (attributeFilters === null) {
|
||||
return null;
|
||||
}
|
||||
const personActions = await getActionsByPersonId(person.id);
|
||||
const personActionClassIds = Array.from(
|
||||
new Set(personActions?.map((action) => action.actionClass?.id ?? ""))
|
||||
);
|
||||
|
||||
// if there are no attribute filters, we return the survey
|
||||
if (!attributeFilters.length) {
|
||||
const attributes = await getAttributes(person.id);
|
||||
const personUserId = person.userId;
|
||||
|
||||
// the surveys now have segment filters, so we need to evaluate them
|
||||
const surveyPromises = surveys.map(async (survey) => {
|
||||
const { segment } = survey;
|
||||
// if the survey has no segment, or the segment has no filters, we return the survey
|
||||
if (!segment || !segment.filters?.length) {
|
||||
return survey;
|
||||
}
|
||||
|
||||
// we check if the person meets the attribute filters for all the attribute filters
|
||||
const isEligible = attributeFilters.every((attributeFilter) => {
|
||||
const personAttributeValue = attributes[attributeFilter.attributeClassName];
|
||||
if (!personAttributeValue) {
|
||||
return false;
|
||||
// backwards compatibility for older versions of the js package
|
||||
// if the version is not provided, we will use the old method of evaluating the segment, which is attribute filters
|
||||
// transform the segment filters to attribute filters and evaluate them
|
||||
if (!options?.version) {
|
||||
const attributeFilters = transformSegmentFiltersToAttributeFilters(segment.filters);
|
||||
|
||||
// if the attribute filters are null, it means the segment filters don't match the expected format for attribute filters, so we skip this survey
|
||||
if (attributeFilters === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (attributeFilter.operator === "equals") {
|
||||
return personAttributeValue === attributeFilter.value;
|
||||
} else if (attributeFilter.operator === "notEquals") {
|
||||
return personAttributeValue !== attributeFilter.value;
|
||||
} else {
|
||||
// if the operator is not equals or not equals, we skip the survey, this means that new segment filter options are being used
|
||||
return false;
|
||||
// if there are no attribute filters, we return the survey
|
||||
if (!attributeFilters.length) {
|
||||
return survey;
|
||||
}
|
||||
});
|
||||
|
||||
return isEligible ? survey : null;
|
||||
// we check if the person meets the attribute filters for all the attribute filters
|
||||
const isEligible = attributeFilters.every((attributeFilter) => {
|
||||
const personAttributeValue = attributes[attributeFilter.attributeClassName];
|
||||
if (!personAttributeValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attributeFilter.operator === "equals") {
|
||||
return personAttributeValue === attributeFilter.value;
|
||||
} else if (attributeFilter.operator === "notEquals") {
|
||||
return personAttributeValue !== attributeFilter.value;
|
||||
} else {
|
||||
// if the operator is not equals or not equals, we skip the survey, this means that new segment filter options are being used
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return isEligible ? survey : null;
|
||||
}
|
||||
|
||||
// Evaluate the segment filters
|
||||
const result = await evaluateSegment(
|
||||
{
|
||||
attributes: attributes ?? {},
|
||||
actionIds: personActionClassIds,
|
||||
deviceType,
|
||||
environmentId,
|
||||
personId: person.id,
|
||||
userId: personUserId,
|
||||
},
|
||||
segment.filters
|
||||
);
|
||||
|
||||
return result ? survey : null;
|
||||
});
|
||||
|
||||
const resolvedSurveys = await Promise.all(surveyPromises);
|
||||
surveys = resolvedSurveys.filter((survey) => !!survey) as TSurvey[];
|
||||
|
||||
if (!surveys) {
|
||||
throw new ResourceNotFoundError("Survey", environmentId);
|
||||
}
|
||||
return surveys;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
// Evaluate the segment filters
|
||||
const result = await evaluateSegment(
|
||||
{
|
||||
attributes: attributes ?? {},
|
||||
actionIds: personActionClassIds,
|
||||
deviceType,
|
||||
environmentId,
|
||||
personId: person.id,
|
||||
userId: personUserId,
|
||||
},
|
||||
segment.filters
|
||||
);
|
||||
|
||||
return result ? survey : null;
|
||||
});
|
||||
|
||||
const resolvedSurveys = await Promise.all(surveyPromises);
|
||||
surveys = resolvedSurveys.filter((survey) => !!survey) as TSurvey[];
|
||||
|
||||
if (!surveys) {
|
||||
throw new ResourceNotFoundError("Survey", environmentId);
|
||||
throw error;
|
||||
}
|
||||
return surveys;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
console.error(error);
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getSyncSurveys-${environmentId}-${personId}`],
|
||||
{
|
||||
tags: [
|
||||
personCache.tag.byEnvironmentId(environmentId),
|
||||
personCache.tag.byId(personId),
|
||||
displayCache.tag.byPersonId(personId),
|
||||
surveyCache.tag.byEnvironmentId(environmentId),
|
||||
productCache.tag.byEnvironmentId(environmentId),
|
||||
attributeCache.tag.byPersonId(personId),
|
||||
],
|
||||
}
|
||||
},
|
||||
[`getSyncSurveys-${environmentId}-${personId}`],
|
||||
{
|
||||
tags: [
|
||||
personCache.tag.byEnvironmentId(environmentId),
|
||||
personCache.tag.byId(personId),
|
||||
displayCache.tag.byPersonId(personId),
|
||||
surveyCache.tag.byEnvironmentId(environmentId),
|
||||
productCache.tag.byEnvironmentId(environmentId),
|
||||
attributeCache.tag.byPersonId(personId),
|
||||
],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getSurveyIdByResultShareKey = async (resultShareKey: string): Promise<string | null> => {
|
||||
try {
|
||||
const survey = await prisma.survey.findFirst({
|
||||
where: {
|
||||
resultShareKey,
|
||||
export const getSurveyIdByResultShareKey = reactCache(
|
||||
(resultShareKey: string): Promise<string | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const survey = await prisma.survey.findFirst({
|
||||
where: {
|
||||
resultShareKey,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!survey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return survey.id;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!survey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return survey.id;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
[`getSurveyIdByResultShareKey-${resultShareKey}`],
|
||||
{
|
||||
tags: [surveyCache.tag.byResultShareKey(resultShareKey)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const loadNewSegmentInSurvey = async (surveyId: string, newSegmentId: string): Promise<TSurvey> => {
|
||||
validateInputs([surveyId, ZId], [newSegmentId, ZId]);
|
||||
@@ -1091,33 +1115,35 @@ export const loadNewSegmentInSurvey = async (surveyId: string, newSegmentId: str
|
||||
}
|
||||
};
|
||||
|
||||
export const getSurveysBySegmentId = (segmentId: string): Promise<TSurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const surveysPrisma = await prisma.survey.findMany({
|
||||
where: { segmentId },
|
||||
select: selectSurvey,
|
||||
});
|
||||
export const getSurveysBySegmentId = reactCache(
|
||||
(segmentId: string): Promise<TSurvey[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
try {
|
||||
const surveysPrisma = await prisma.survey.findMany({
|
||||
where: { segmentId },
|
||||
select: selectSurvey,
|
||||
});
|
||||
|
||||
const surveys: TSurvey[] = [];
|
||||
const surveys: TSurvey[] = [];
|
||||
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
for (const surveyPrisma of surveysPrisma) {
|
||||
const transformedSurvey = transformPrismaSurvey(surveyPrisma);
|
||||
surveys.push(transformedSurvey);
|
||||
}
|
||||
|
||||
return surveys;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return surveys;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getSurveysBySegmentId-${segmentId}`],
|
||||
{
|
||||
tags: [surveyCache.tag.bySegmentId(segmentId), segmentCache.tag.byId(segmentId)],
|
||||
}
|
||||
},
|
||||
[`getSurveysBySegmentId-${segmentId}`],
|
||||
{
|
||||
tags: [surveyCache.tag.bySegmentId(segmentId), segmentCache.tag.byId(segmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
25
packages/lib/tag/cache.ts
Normal file
25
packages/lib/tag/cache.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { revalidateTag } from "next/cache";
|
||||
|
||||
interface RevalidateProps {
|
||||
id?: string;
|
||||
environmentId?: string;
|
||||
}
|
||||
|
||||
export const tagCache = {
|
||||
tag: {
|
||||
byId(id: string) {
|
||||
return `tags-${id}`;
|
||||
},
|
||||
byEnvironmentId(environmentId: string) {
|
||||
return `environments-${environmentId}-tags`;
|
||||
},
|
||||
},
|
||||
revalidate({ id, environmentId }: RevalidateProps): void {
|
||||
if (id) {
|
||||
revalidateTag(this.tag.byId(id));
|
||||
}
|
||||
if (environmentId) {
|
||||
revalidateTag(this.tag.byEnvironmentId(environmentId));
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,44 +1,65 @@
|
||||
import "server-only";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
import { cache } from "../cache";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { tagCache } from "./cache";
|
||||
|
||||
export const getTagsByEnvironmentId = async (environmentId: string, page?: number): Promise<TTag[]> => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
export const getTagsByEnvironmentId = reactCache(
|
||||
(environmentId: string, page?: number): Promise<TTag[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
|
||||
|
||||
try {
|
||||
const tags = await prisma.tag.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
try {
|
||||
const tags = await prisma.tag.findMany({
|
||||
where: {
|
||||
environmentId,
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
|
||||
return tags;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
take: page ? ITEMS_PER_PAGE : undefined,
|
||||
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
|
||||
});
|
||||
[`getTagsByEnvironmentId-${environmentId}-${page}`],
|
||||
{
|
||||
tags: [tagCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
return tags;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
export const getTag = reactCache(
|
||||
(id: string): Promise<TTag | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([id, ZId]);
|
||||
|
||||
export const getTag = async (tagId: string): Promise<TTag | null> => {
|
||||
validateInputs([tagId, ZId]);
|
||||
try {
|
||||
const tag = await prisma.tag.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const tag = await prisma.tag.findUnique({
|
||||
where: {
|
||||
id: tagId,
|
||||
return tag;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return tag;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
[`getTag-${id}`],
|
||||
{
|
||||
tags: [tagCache.tag.byId(id)],
|
||||
}
|
||||
)()
|
||||
);
|
||||
|
||||
export const createTag = async (environmentId: string, name: string): Promise<TTag> => {
|
||||
validateInputs([environmentId, ZId], [name, ZString]);
|
||||
@@ -51,20 +72,9 @@ export const createTag = async (environmentId: string, name: string): Promise<TT
|
||||
},
|
||||
});
|
||||
|
||||
return tag;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteTag = async (tagId: string): Promise<TTag> => {
|
||||
validateInputs([tagId, ZId]);
|
||||
|
||||
try {
|
||||
const tag = await prisma.tag.delete({
|
||||
where: {
|
||||
id: tagId,
|
||||
},
|
||||
tagCache.revalidate({
|
||||
id: tag.id,
|
||||
environmentId,
|
||||
});
|
||||
|
||||
return tag;
|
||||
@@ -73,19 +83,45 @@ export const deleteTag = async (tagId: string): Promise<TTag> => {
|
||||
}
|
||||
};
|
||||
|
||||
export const updateTagName = async (tagId: string, name: string): Promise<TTag> => {
|
||||
validateInputs([tagId, ZId], [name, ZString]);
|
||||
export const deleteTag = async (id: string): Promise<TTag> => {
|
||||
validateInputs([id, ZId]);
|
||||
|
||||
try {
|
||||
const tag = await prisma.tag.delete({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
tagCache.revalidate({
|
||||
id,
|
||||
environmentId: tag.environmentId,
|
||||
});
|
||||
|
||||
return tag;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateTagName = async (id: string, name: string): Promise<TTag> => {
|
||||
validateInputs([id, ZId], [name, ZString]);
|
||||
|
||||
try {
|
||||
const tag = await prisma.tag.update({
|
||||
where: {
|
||||
id: tagId,
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
},
|
||||
});
|
||||
|
||||
tagCache.revalidate({
|
||||
id: tag.id,
|
||||
environmentId: tag.environmentId,
|
||||
});
|
||||
|
||||
return tag;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@@ -206,6 +242,15 @@ export const mergeTags = async (originalTagId: string, newTagId: string): Promis
|
||||
}),
|
||||
]);
|
||||
|
||||
tagCache.revalidate({
|
||||
id: originalTagId,
|
||||
environmentId: originalTag.environmentId,
|
||||
});
|
||||
|
||||
tagCache.revalidate({
|
||||
id: newTagId,
|
||||
});
|
||||
|
||||
return newTag;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
@@ -91,38 +92,40 @@ export const deleteTagOnResponse = async (responseId: string, tagId: string): Pr
|
||||
}
|
||||
};
|
||||
|
||||
export const getTagsOnResponsesCount = async (environmentId: string): Promise<TTagsCount> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
export const getTagsOnResponsesCount = reactCache(
|
||||
(environmentId: string): Promise<TTagsCount> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
try {
|
||||
const tagsCount = await prisma.tagsOnResponses.groupBy({
|
||||
by: ["tagId"],
|
||||
where: {
|
||||
response: {
|
||||
survey: {
|
||||
environment: {
|
||||
id: environmentId,
|
||||
try {
|
||||
const tagsCount = await prisma.tagsOnResponses.groupBy({
|
||||
by: ["tagId"],
|
||||
where: {
|
||||
response: {
|
||||
survey: {
|
||||
environment: {
|
||||
id: environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
});
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
});
|
||||
|
||||
return tagsCount.map((tagCount) => ({ tagId: tagCount.tagId, count: tagCount._count._all }));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return tagsCount.map((tagCount) => ({ tagId: tagCount.tagId, count: tagCount._count._all }));
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
},
|
||||
[`getTagsOnResponsesCount-${environmentId}`],
|
||||
{
|
||||
tags: [tagOnResponseCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
},
|
||||
[`getTagsOnResponsesCount-${environmentId}`],
|
||||
{
|
||||
tags: [tagOnResponseCache.tag.byEnvironmentId(environmentId)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
@@ -29,64 +30,68 @@ const responseSelection = {
|
||||
};
|
||||
|
||||
// function to retrive basic information about a user's user
|
||||
export const getUser = (id: string): Promise<TUser | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([id, ZId]);
|
||||
export const getUser = reactCache(
|
||||
(id: string): Promise<TUser | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([id, ZId]);
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getUser-${id}`],
|
||||
{
|
||||
tags: [userCache.tag.byId(id)],
|
||||
}
|
||||
},
|
||||
[`getUser-${id}`],
|
||||
{
|
||||
tags: [userCache.tag.byId(id)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
export const getUserByEmail = (email: string): Promise<TUser | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([email, z.string().email()]);
|
||||
export const getUserByEmail = reactCache(
|
||||
(email: string): Promise<TUser | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([email, z.string().email()]);
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
try {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
select: responseSelection,
|
||||
});
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
[`getUserByEmail-${email}`],
|
||||
{
|
||||
tags: [userCache.tag.byEmail(email)],
|
||||
}
|
||||
},
|
||||
[`getUserByEmail-${email}`],
|
||||
{
|
||||
tags: [userCache.tag.byEmail(email)],
|
||||
}
|
||||
)();
|
||||
)()
|
||||
);
|
||||
|
||||
const getAdminMemberships = (memberships: TMembership[]): TMembership[] =>
|
||||
memberships.filter((membership) => membership.role === "admin");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import z from "zod";
|
||||
import { z } from "zod";
|
||||
import { ValidationError } from "@formbricks/types/errors";
|
||||
|
||||
type ValidationPair = [any, z.ZodSchema<any>];
|
||||
|
||||
@@ -10,6 +10,18 @@ vi.mock("next/cache", () => ({
|
||||
revalidateTag: vi.fn(),
|
||||
}));
|
||||
|
||||
// mock react cache
|
||||
const testCache = <T extends Function>(func: T) => func;
|
||||
|
||||
vi.mock("react", () => {
|
||||
const originalModule = vi.importActual("react");
|
||||
return {
|
||||
...originalModule,
|
||||
cache: testCache,
|
||||
};
|
||||
});
|
||||
|
||||
// mock server-only
|
||||
vi.mock("server-only", () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user