diff --git a/app/api/route.ts b/app/api/route.ts
index f779a8af..d3dcde6d 100644
--- a/app/api/route.ts
+++ b/app/api/route.ts
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
import { calculationSchema } from "../schemas/calculationSchema";
import * as calculationService from "../services/calculationService";
import calculateFairhold from "../models/testClasses";
+import { APIError } from "../lib/exceptions";
export async function POST(req: Request) {
try {
@@ -11,9 +12,22 @@ export async function POST(req: Request) {
const householdData = await calculationService.getHouseholdData(input);
const processedData = calculateFairhold(householdData);
return NextResponse.json(processedData);
- } catch (err) {
- console.log("ERROR: API - ", (err as Error).message);
- const response = { error: (err as Error).message };
+ } catch (error) {
+ console.log("ERROR: API - ", (error as Error).message);
+
+ if (error instanceof APIError) {
+ return NextResponse.json(
+ { error },
+ { status: error.status },
+ );
+ }
+
+ const response = {
+ error: {
+ message: (error as Error).message,
+ code: "UNHANDLED_EXCEPTION"
+ },
+ };
return NextResponse.json(response, { status: 500 });
}
}
diff --git a/app/components/ui/CalculatorInput.tsx b/app/components/ui/CalculatorInput.tsx
index 53c64c21..841234e3 100644
--- a/app/components/ui/CalculatorInput.tsx
+++ b/app/components/ui/CalculatorInput.tsx
@@ -21,6 +21,7 @@ import {
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
+import { APIError } from "@/app/lib/exceptions";
type View = "form" | "loading" | "dashboard";
@@ -68,10 +69,31 @@ const CalculatorInput = () => {
body: JSON.stringify(data),
});
const processedData = await response.json();
+ if (!response.ok) return handleErrors(processedData.error);
+
setData(processedData);
setView("dashboard");
};
+ const handleErrors = (error: APIError) => {
+ switch (error.code) {
+ case "ITL3_NOT_FOUND":
+ case "INSUFFICIENT_PRICES_PAID_DATA":
+ methods.setError(
+ "housePostcode",
+ { message:
+ "Insufficient data for this postcode. Please try again with a different postcode"
+ }
+ );
+ break;
+ case "UNHANDLED_EXCEPTION":
+ default:
+ console.error(error)
+ };
+
+ setView("form");
+ }
+
if (view === "form") {
return (
diff --git a/app/data/itlRepo.test.ts b/app/data/itlRepo.test.ts
index 21031a4e..fe005af1 100644
--- a/app/data/itlRepo.test.ts
+++ b/app/data/itlRepo.test.ts
@@ -46,7 +46,7 @@ describe("itlRepo", () => {
await expect(
itlRepo.getItl3ByPostcodeDistrict(postcodeDistrict)
).rejects.toThrow(
- `Data error: Unable get get itl3 for postcode district ${postcodeDistrict}`
+ `Data error: Unable get itl3 for postcode district ${postcodeDistrict}`
);
});
});
diff --git a/app/data/itlRepo.ts b/app/data/itlRepo.ts
index 792e3670..1e2982e5 100644
--- a/app/data/itlRepo.ts
+++ b/app/data/itlRepo.ts
@@ -1,4 +1,5 @@
import prisma from "./db";
+import { APIError } from "../lib/exceptions";
const getItl3ByPostcodeDistrict = async (
postcodeDistrict: string
@@ -15,10 +16,11 @@ const getItl3ByPostcodeDistrict = async (
return itl3;
} catch (error) {
- throw new Error(
- `Data error: Unable get get itl3 for postcode district ${postcodeDistrict}`
- );
-
+ throw new APIError({
+ status: 500,
+ message: `Data error: Unable get itl3 for postcode district ${postcodeDistrict}`,
+ code: "ITL3_NOT_FOUND"
+ });
}
};
diff --git a/app/data/pricesPaidRepo.ts b/app/data/pricesPaidRepo.ts
index fac7a8f3..2dd8b995 100644
--- a/app/data/pricesPaidRepo.ts
+++ b/app/data/pricesPaidRepo.ts
@@ -1,3 +1,4 @@
+import { APIError } from "../lib/exceptions";
import prisma from "./db";
const MINIMUM_NUMBER_OF_POSTCODES = 30;
@@ -32,7 +33,11 @@ const getPricesPaidByPostcodeAndHouseType = async (
});
if (!summary) {
- throw new Error("Unable to find sufficient transaction data");
+ throw new APIError({
+ status: 500,
+ message: "Unable to find sufficient transaction data",
+ code: "INSUFFICIENT_PRICES_PAID_DATA",
+ });
}
return {
diff --git a/app/lib/exceptions.ts b/app/lib/exceptions.ts
new file mode 100644
index 00000000..48ef7a13
--- /dev/null
+++ b/app/lib/exceptions.ts
@@ -0,0 +1,22 @@
+export type APIErrorCode =
+ | "ITL3_NOT_FOUND"
+ | "INSUFFICIENT_PRICES_PAID_DATA"
+ | "UNHANDLED_EXCEPTION";
+
+interface APIErrorParams {
+ code: APIErrorCode;
+ message: string;
+ status?: number;
+}
+
+export class APIError extends Error {
+ public code: APIErrorCode;
+ public status: number;
+
+ constructor({ code, message, status = 500 }: APIErrorParams) {
+ super(message);
+ this.name = "APIError";
+ this.code = code;
+ this.status = status;
+ }
+}
diff --git a/app/services/calculationService.ts b/app/services/calculationService.ts
index 0ed97184..3f3539e2 100644
--- a/app/services/calculationService.ts
+++ b/app/services/calculationService.ts
@@ -10,6 +10,7 @@ import { socialRentEarningsService } from "./socialRentEarningsService";
import { rentService } from "./rentService"
import { Calculation } from "../schemas/calculationSchema";
+import { APIError } from "../lib/exceptions";
const prisma = new PrismaClient();
@@ -63,6 +64,8 @@ export const getHouseholdData = async (input: Calculation) => {
};
} catch (err) {
+ if (err instanceof APIError) throw err;
+
throw Error(
`Service error: Unable to generate household. Message: ${(err as Error).message}`
);