mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-05 13:21:36 -05:00
fix: api errors (#3150)
Co-authored-by: Piyush Gupta <piyushguptaa2z123@gmail.com>
This commit is contained in:
@@ -40,16 +40,16 @@ export const getEnvironmentState = async (
|
||||
getProductByEnvironmentId(environmentId),
|
||||
]);
|
||||
|
||||
if (!organization) {
|
||||
throw new ResourceNotFoundError("organization", environmentId);
|
||||
}
|
||||
|
||||
if (!environment) {
|
||||
throw new ResourceNotFoundError("environment", environmentId);
|
||||
}
|
||||
|
||||
if (!organization) {
|
||||
throw new ResourceNotFoundError("organization", null);
|
||||
}
|
||||
|
||||
if (!product) {
|
||||
throw new ResourceNotFoundError("product", environmentId);
|
||||
throw new ResourceNotFoundError("product", null);
|
||||
}
|
||||
|
||||
if (product.config.channel && product.config.channel !== "app") {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { responses } from "@/app/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||
import { NextRequest } from "next/server";
|
||||
import { environmentCache } from "@formbricks/lib/environment/cache";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { ZJsSyncInput } from "@formbricks/types/js";
|
||||
|
||||
export const OPTIONS = async (): Promise<Response> => {
|
||||
@@ -49,6 +50,10 @@ export const GET = async (
|
||||
"public, s-maxage=600, max-age=840, stale-while-revalidate=600, stale-if-error=600"
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof ResourceNotFoundError) {
|
||||
return responses.notFoundResponse(err.resourceType, err.resourceId);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
return responses.internalServerErrorResponse(err.message, true);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { responses } from "@/app/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||
import { NextRequest, userAgent } from "next/server";
|
||||
import { personCache } from "@formbricks/lib/person/cache";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { ZJsPersonIdentifyInput } from "@formbricks/types/js";
|
||||
import { getPersonState } from "./lib/personState";
|
||||
|
||||
@@ -53,6 +54,10 @@ export const GET = async (
|
||||
"public, s-maxage=600, max-age=840, stale-while-revalidate=600, stale-if-error=600"
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof ResourceNotFoundError) {
|
||||
return responses.notFoundResponse(err.resourceType, err.resourceId);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
return responses.internalServerErrorResponse(err.message ?? "Unable to fetch person state", true);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||
import { NextRequest } from "next/server";
|
||||
import { getAttributesByUserId, updateAttributes } from "@formbricks/lib/attribute/service";
|
||||
import { createPerson, getPersonByUserId } from "@formbricks/lib/person/service";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { ZJsPeopleUpdateAttributeInput } from "@formbricks/types/js";
|
||||
|
||||
export const OPTIONS = async () => {
|
||||
@@ -81,6 +82,10 @@ export const PUT = async (
|
||||
return responses.forbiddenResponse(err.message || "Forbidden", true, { ignore: true });
|
||||
}
|
||||
|
||||
if (err instanceof ResourceNotFoundError) {
|
||||
return responses.notFoundResponse(err.resourceType, err.resourceId, true);
|
||||
}
|
||||
|
||||
return responses.internalServerErrorResponse("Something went wrong", true);
|
||||
}
|
||||
};
|
||||
|
||||
+5
-5
@@ -40,16 +40,16 @@ export const getEnvironmentState = async (
|
||||
getProductByEnvironmentId(environmentId),
|
||||
]);
|
||||
|
||||
if (!organization) {
|
||||
throw new ResourceNotFoundError("organization", environmentId);
|
||||
}
|
||||
|
||||
if (!environment) {
|
||||
throw new ResourceNotFoundError("environment", environmentId);
|
||||
}
|
||||
|
||||
if (!organization) {
|
||||
throw new ResourceNotFoundError("organization", null);
|
||||
}
|
||||
|
||||
if (!product) {
|
||||
throw new ResourceNotFoundError("product", environmentId);
|
||||
throw new ResourceNotFoundError("product", null);
|
||||
}
|
||||
|
||||
if (product.config.channel && product.config.channel !== "website") {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { responses } from "@/app/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||
import { NextRequest } from "next/server";
|
||||
import { environmentCache } from "@formbricks/lib/environment/cache";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { ZJsSyncInput } from "@formbricks/types/js";
|
||||
import { getEnvironmentState } from "./lib/environmentState";
|
||||
|
||||
@@ -44,6 +45,11 @@ export const GET = async (
|
||||
"public, s-maxage=600, max-age=840, stale-while-revalidate=600, stale-if-error=600"
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof ResourceNotFoundError) {
|
||||
return responses.notFoundResponse(err.resourceType, err.resourceId);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
return responses.internalServerErrorResponse(err.message ?? "Unable to complete response", true);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -106,7 +106,7 @@ const methodNotAllowedResponse = (
|
||||
|
||||
const notFoundResponse = (
|
||||
resourceType: string,
|
||||
resourceId: string,
|
||||
resourceId: string | null,
|
||||
cors: boolean = false,
|
||||
cache: string = "private, no-store"
|
||||
) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { selectDisplay } from "../../service";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
|
||||
export const mockEnvironmentId = "clqkr5961000108jyfnjmbjhi";
|
||||
export const mockSingleUseId = "qj57j3opsw8b5sxgea20fgcq";
|
||||
@@ -50,3 +49,13 @@ export const mockDisplayUpdate = {
|
||||
userId: mockUserId,
|
||||
responseId: mockResponseId,
|
||||
};
|
||||
|
||||
export const mockEnvironment: TEnvironment = {
|
||||
id: mockId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
type: "production",
|
||||
productId: mockId,
|
||||
appSetupCompleted: false,
|
||||
websiteSetupCompleted: false,
|
||||
};
|
||||
|
||||
@@ -7,13 +7,14 @@ import {
|
||||
mockDisplayUpdate,
|
||||
mockDisplayWithPersonId,
|
||||
mockDisplayWithResponseId,
|
||||
mockEnvironment,
|
||||
mockResponseId,
|
||||
mockSurveyId,
|
||||
} from "./__mocks__/data.mock";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { testInputValidation } from "vitestSetup";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import {
|
||||
createDisplay,
|
||||
deleteDisplayByResponseId,
|
||||
@@ -94,6 +95,7 @@ describe("Tests for getDisplay", () => {
|
||||
describe("Tests for createDisplay service", () => {
|
||||
describe("Happy Path", () => {
|
||||
it("Creates a new display when a userId exists", async () => {
|
||||
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
|
||||
prisma.display.create.mockResolvedValue(mockDisplayWithPersonId);
|
||||
|
||||
const display = await createDisplay(mockDisplayInputWithUserId);
|
||||
@@ -113,6 +115,7 @@ describe("Tests for createDisplay service", () => {
|
||||
|
||||
it("Throws DatabaseError on PrismaClientKnownRequestError occurrence", async () => {
|
||||
const mockErrorMessage = "Mock error message";
|
||||
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
|
||||
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
|
||||
code: "P2002",
|
||||
clientVersion: "0.0.1",
|
||||
@@ -136,6 +139,7 @@ describe("Tests for updateDisplay Service", () => {
|
||||
describe("Happy Path", () => {
|
||||
it("Updates a display (responded)", async () => {
|
||||
prisma.display.update.mockResolvedValue(mockDisplayWithResponseId);
|
||||
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
|
||||
|
||||
const display = await updateDisplay(mockDisplay.id, mockDisplayUpdate);
|
||||
expect(display).toEqual(mockDisplayWithResponseId);
|
||||
@@ -146,6 +150,7 @@ describe("Tests for updateDisplay Service", () => {
|
||||
testInputValidation(updateDisplay, "123", "123");
|
||||
|
||||
it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
|
||||
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
|
||||
const mockErrorMessage = "Mock error message";
|
||||
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
|
||||
code: "P2002",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { TPerson } from "@formbricks/types/people";
|
||||
import { cache } from "../cache";
|
||||
import { ITEMS_PER_PAGE } from "../constants";
|
||||
@@ -218,6 +218,16 @@ export const getPersonByUserId = reactCache(
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId], [userId, ZString]);
|
||||
|
||||
const environment = await prisma.environment.findUnique({
|
||||
where: {
|
||||
id: environmentId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!environment) {
|
||||
throw new ResourceNotFoundError("environment", environmentId);
|
||||
}
|
||||
|
||||
// check if userId exists as a column
|
||||
const personWithUserId = await prisma.person.findFirst({
|
||||
where: {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
|
||||
export const mockId = "ars2tjk8hsi8oqk1uac00mo8";
|
||||
|
||||
export const constantsForTests = {
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
browser: "Chrome",
|
||||
@@ -6,3 +10,13 @@ export const constantsForTests = {
|
||||
text: "Abc12345",
|
||||
fullName: "Pavitr Prabhakar",
|
||||
};
|
||||
|
||||
export const mockEnvironment: TEnvironment = {
|
||||
id: mockId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
type: "production",
|
||||
productId: mockId,
|
||||
appSetupCompleted: false,
|
||||
websiteSetupCompleted: false,
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
updateResponse,
|
||||
} from "../service";
|
||||
import { buildWhereClause } from "../utils";
|
||||
import { constantsForTests } from "./constants";
|
||||
import { constantsForTests, mockEnvironment } from "./constants";
|
||||
|
||||
// vitest.mock("../../organization/service", async (methods) => {
|
||||
// return {
|
||||
@@ -205,6 +205,7 @@ describe("Tests for createResponse service", () => {
|
||||
describe("Happy Path", () => {
|
||||
it("Creates a response linked to an existing user", async () => {
|
||||
prisma.attribute.findMany.mockResolvedValue([]);
|
||||
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
|
||||
const response = await createResponse(mockResponseInputWithUserId);
|
||||
expect(response).toEqual(expectedResponseWithPerson);
|
||||
});
|
||||
@@ -216,6 +217,7 @@ describe("Tests for createResponse service", () => {
|
||||
|
||||
it("Creates a new person and response when the person does not exist", async () => {
|
||||
prisma.person.findFirst.mockResolvedValue(null);
|
||||
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
|
||||
prisma.person.create.mockResolvedValue(mockPerson);
|
||||
prisma.attribute.findMany.mockResolvedValue([]);
|
||||
|
||||
@@ -246,6 +248,7 @@ describe("Tests for createResponse service", () => {
|
||||
clientVersion: "0.0.1",
|
||||
});
|
||||
|
||||
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
|
||||
prisma.response.create.mockRejectedValue(errToThrow);
|
||||
prisma.attribute.findMany.mockResolvedValue([]);
|
||||
|
||||
|
||||
@@ -2,9 +2,14 @@ import { z } from "zod";
|
||||
|
||||
class ResourceNotFoundError extends Error {
|
||||
statusCode = 404;
|
||||
constructor(resource: string, id: string) {
|
||||
super(`${resource} with ID ${id} not found`);
|
||||
resourceId: string | null;
|
||||
resourceType: string;
|
||||
|
||||
constructor(resource: string, id: string | null) {
|
||||
super(id ? `${resource} with ID ${id} not found` : `${resource} not found`);
|
||||
this.name = "ResourceNotFoundError";
|
||||
this.resourceType = resource;
|
||||
this.resourceId = id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user