Skip to content

Commit

Permalink
feat: Gracefully handle non-English postcodes (#244)
Browse files Browse the repository at this point in the history
* feat: Error handling for non-English postcodes

* fix: Typos
  • Loading branch information
DafyddLlyr authored Jan 9, 2025
1 parent 2a26cf3 commit 7769894
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 9 deletions.
20 changes: 17 additions & 3 deletions app/api/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 });
}
}
22 changes: 22 additions & 0 deletions app/components/ui/CalculatorInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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 (
<div className="flex items-center justify-center text-black mt-5">
Expand Down
2 changes: 1 addition & 1 deletion app/data/itlRepo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
);
});
});
10 changes: 6 additions & 4 deletions app/data/itlRepo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import prisma from "./db";
import { APIError } from "../lib/exceptions";

const getItl3ByPostcodeDistrict = async (
postcodeDistrict: string
Expand All @@ -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"
});
}
};

Expand Down
7 changes: 6 additions & 1 deletion app/data/pricesPaidRepo.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { APIError } from "../lib/exceptions";
import prisma from "./db";

const MINIMUM_NUMBER_OF_POSTCODES = 30;
Expand Down Expand Up @@ -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 {
Expand Down
22 changes: 22 additions & 0 deletions app/lib/exceptions.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
3 changes: 3 additions & 0 deletions app/services/calculationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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}`
);
Expand Down

0 comments on commit 7769894

Please sign in to comment.