From e0502a6527667ea3d248513dce38218327bd9930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 2 Aug 2024 09:10:15 +0100 Subject: [PATCH 1/7] fix: Rebase issues --- app/api/route.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/api/route.ts b/app/api/route.ts index 48affcd..ebb94ff 100644 --- a/app/api/route.ts +++ b/app/api/route.ts @@ -202,7 +202,6 @@ export async function POST(req: Request) { buildPrice, averageRentMonthly, socialRentAdjustments, - socialRentAveEarning, numberOfTransactions, granularityPostcode, pricesPaid, From cd1e030705fdf415d28d8fac9c3cc5920dbc1ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 2 Aug 2024 13:52:53 +0100 Subject: [PATCH 2/7] chore: Tidy up --- app/api/route.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/api/route.ts b/app/api/route.ts index ebb94ff..48affcd 100644 --- a/app/api/route.ts +++ b/app/api/route.ts @@ -202,6 +202,7 @@ export async function POST(req: Request) { buildPrice, averageRentMonthly, socialRentAdjustments, + socialRentAveEarning, numberOfTransactions, granularityPostcode, pricesPaid, From b637cb89a9f7ef49b93b34d4257f33957e661356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Sat, 3 Aug 2024 11:32:42 +0100 Subject: [PATCH 3/7] feat: Setup DB singleton --- app/data/db.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 app/data/db.ts diff --git a/app/data/db.ts b/app/data/db.ts new file mode 100644 index 0000000..e5d7482 --- /dev/null +++ b/app/data/db.ts @@ -0,0 +1,15 @@ +import { PrismaClient } from "@prisma/client"; + +const prismaClientSingleton = () => { + return new PrismaClient(); +}; + +declare const globalThis: { + prismaGlobal: ReturnType; +} & typeof global; + +const prisma = globalThis.prismaGlobal ?? prismaClientSingleton(); + +export default prisma; + +if (process.env.NODE_ENV !== "production") globalThis.prismaGlobal = prisma; From 8c45fc7b1ea04a872f27d65bc793d9f7e968664e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Sat, 3 Aug 2024 11:34:31 +0100 Subject: [PATCH 4/7] feat: ITLLookup --- app/api/route.ts | 14 ++------------ app/data/itlLookupRepo.ts | 24 ++++++++++++++++++++++++ app/services/itlLookupService.ts | 5 +++++ 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 app/data/itlLookupRepo.ts create mode 100644 app/services/itlLookupService.ts diff --git a/app/api/route.ts b/app/api/route.ts index 48affcd..8df0bd1 100644 --- a/app/api/route.ts +++ b/app/api/route.ts @@ -1,6 +1,7 @@ import { NextResponse } from "next/server"; import { PrismaClient } from "@prisma/client"; import { Calculation, calculationSchema } from "../schemas/calculationSchema"; +import * as itl3Service from "../services/itlLookupService"; const prisma = new PrismaClient(); @@ -114,18 +115,7 @@ export async function POST(req: Request) { // TODO: Make columns non-nullable if (!buildPrice) throw Error("Missing buildPrice"); - const { itl3 } = await prisma.itlLookup.findFirstOrThrow({ - where: { - postcode: postcodeDistrict, - itl3: { - not: null, - }, - }, - select: { - itl3: true, - }, - }); - if (!itl3) throw Error("Missing itl3"); + const itl3 = await itl3Service.getByPostcodeDistrict(postcodeDistrict); const { gdhi2020: gdhi } = await prisma.gDHI.findFirstOrThrow({ where: { diff --git a/app/data/itlLookupRepo.ts b/app/data/itlLookupRepo.ts new file mode 100644 index 0000000..d72e518 --- /dev/null +++ b/app/data/itlLookupRepo.ts @@ -0,0 +1,24 @@ +import prisma from "./db"; + +export const getItl3ByPostcodeDistrict = async ( + postcodeDistrict: string +): Promise => { + try { + const { itl3 } = await prisma.itlLookup.findFirstOrThrow({ + where: { + postcode: postcodeDistrict, + itl3: { + not: null, + }, + }, + select: { + itl3: true, + }, + }); + + // Cast to string as 'not: null' clause in Prisma query does not type narrow + return itl3 as string; + } catch (error) { + throw new Error(`Data error: Unable get get itl3 for postcode district ${postcodeDistrict}`); + } +}; diff --git a/app/services/itlLookupService.ts b/app/services/itlLookupService.ts new file mode 100644 index 0000000..a04a2f1 --- /dev/null +++ b/app/services/itlLookupService.ts @@ -0,0 +1,5 @@ +import * as repo from "../data/itlLookupRepo"; + +export const getByPostcodeDistrict = async (postcodeDistrict: string) => { + return await repo.getItl3ByPostcodeDistrict(postcodeDistrict); +} From c5f359707b371faf0060c343c96f723da9eba80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Sat, 3 Aug 2024 11:54:01 +0100 Subject: [PATCH 5/7] feat: GDHI --- app/api/route.ts | 10 ++-------- app/data/gdhiRepo.ts | 22 ++++++++++++++++++++++ app/services/gdhiService.ts | 5 +++++ 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 app/data/gdhiRepo.ts create mode 100644 app/services/gdhiService.ts diff --git a/app/api/route.ts b/app/api/route.ts index 8df0bd1..f35eb6b 100644 --- a/app/api/route.ts +++ b/app/api/route.ts @@ -2,6 +2,7 @@ import { NextResponse } from "next/server"; import { PrismaClient } from "@prisma/client"; import { Calculation, calculationSchema } from "../schemas/calculationSchema"; import * as itl3Service from "../services/itlLookupService"; +import * as ghdiService from "../services/gdhiService"; const prisma = new PrismaClient(); @@ -116,14 +117,7 @@ export async function POST(req: Request) { if (!buildPrice) throw Error("Missing buildPrice"); const itl3 = await itl3Service.getByPostcodeDistrict(postcodeDistrict); - - const { gdhi2020: gdhi } = await prisma.gDHI.findFirstOrThrow({ - where: { - itl3: { equals: itl3 }, - }, - select: { gdhi2020: true }, - }); - if (!gdhi) throw Error("Missing gdhi"); + const gdhi = await ghdiService.getByITL3(itl3); const { _avg: { monthlyMeanRent: averageRentMonthly }, diff --git a/app/data/gdhiRepo.ts b/app/data/gdhiRepo.ts new file mode 100644 index 0000000..0c649f0 --- /dev/null +++ b/app/data/gdhiRepo.ts @@ -0,0 +1,22 @@ +import prisma from "./db"; + +export const getGDHI2020ByITL3 = async ( + itl3: string +): Promise => { + try { + const { gdhi2020 } = await prisma.gDHI.findFirstOrThrow({ + where: { + AND: { + itl3: { equals: itl3 }, + // TODO: Add `NOT NULL` constraint to column + gdhi2020: { not: null } + }, + }, + select: { gdhi2020: true }, + }); + + return gdhi2020 as number; + } catch (error) { + throw Error(`Data error: Unable to find gdhi2020 for itl3 ${itl3}`); + } +}; diff --git a/app/services/gdhiService.ts b/app/services/gdhiService.ts new file mode 100644 index 0000000..dccbb16 --- /dev/null +++ b/app/services/gdhiService.ts @@ -0,0 +1,5 @@ +import * as repo from './../data/gdhiRepo'; + +export const getByITL3 = async (itl3: string) => { + return await repo.getGDHI2020ByITL3(itl3); +}; From 398a30e79be31510f14e52ea5939b654c12374fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Sat, 3 Aug 2024 12:13:12 +0100 Subject: [PATCH 6/7] feat: Define handler layer by creating calculationService --- app/api/route.ts | 190 +-------------------------- app/services/calculationService.ts | 198 +++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 187 deletions(-) create mode 100644 app/services/calculationService.ts diff --git a/app/api/route.ts b/app/api/route.ts index f35eb6b..57bf881 100644 --- a/app/api/route.ts +++ b/app/api/route.ts @@ -1,202 +1,18 @@ import { NextResponse } from "next/server"; -import { PrismaClient } from "@prisma/client"; import { Calculation, calculationSchema } from "../schemas/calculationSchema"; -import * as itl3Service from "../services/itlLookupService"; -import * as ghdiService from "../services/gdhiService"; +import * as calculationService from "../services/calculationService"; -const prisma = new PrismaClient(); - -// define and export the GET handler function export async function POST(req: Request) { try { // Parse and validate user input const data = await req.json(); const input: Calculation = calculationSchema.parse(data); - // data are going to be queried at different levels of granularity based on the postcode - const postcode = input.housePostcode; - const postcodeArea = postcode.area; // extract only the characters for the area, e.g SE - const postcodeDistrict = postcode.district; // extract only characters for the district, SE17 - const postcodeSector = postcode.sector; // extract only the characters for the sector, SE17 1 - - // create the progressive queries - const minimumNumberPostcodes = 30; // minimum number of entries to create the average - let pricesPaid; // declare the variable for prices paid - let numberOfTransactions; // declare the variable for numbers of transactions retrieved - let granularityPostcode; // declare the granularity of the postcode - let averagePrice; - - const pricesPaidSector = await prisma.pricesPaid.aggregate({ - where: { - propertyType: { - equals: input.houseType, - }, - postcode: { - startsWith: postcodeSector, - }, - }, - _count: { - id: true, - }, - _avg: { - price: true, - }, - }); - - const numberPerSector = pricesPaidSector._count.id; - const isMinMetBySector = numberPerSector >= minimumNumberPostcodes; - - if (!isMinMetBySector) { - const pricesPaidDistrict = await prisma.pricesPaid.aggregate({ - where: { - propertyType: { - equals: input.houseType, - }, - postcode: { - startsWith: postcodeDistrict, - }, - }, - _count: { - id: true, - }, - _avg: { - price: true, - }, - }); - - const numberPerDistrict = pricesPaidDistrict._count.id; - const isMinMetByDistrict = numberPerDistrict >= minimumNumberPostcodes; - - if (!isMinMetByDistrict) { - const pricesPaidArea = await prisma.pricesPaid.aggregate({ - where: { - propertyType: { - equals: input.houseType, - }, - postcode: { - startsWith: postcodeArea, - }, - }, - _count: { - id: true, - }, - _avg: { - price: true, - }, - }); - const numberPerArea = pricesPaidArea._count.id; - - pricesPaid = pricesPaidArea; // if condition is met, the granularity is appropriate - numberOfTransactions = numberPerArea; // check the granularity - granularityPostcode = postcodeArea; // granularity of the postcode when performing the average price search - averagePrice = pricesPaidArea._avg.price; - } else { - pricesPaid = pricesPaidDistrict; // if condition is met, the granularity is appropriate - numberOfTransactions = numberPerDistrict; // check the granularity - granularityPostcode = postcodeDistrict; // granularity of the postcode - averagePrice = pricesPaidDistrict._avg.price; - } - } else { - pricesPaid = pricesPaidSector; // if condition is met, the granularity is appropriate - numberOfTransactions = numberPerSector; // check the granularity - granularityPostcode = postcodeSector; // granularity of the postcode - averagePrice = pricesPaidSector._avg.price; - } - - if (averagePrice === null) { - throw new Error("Unable to calculate average price"); - } - - const { priceMid: buildPrice } = await prisma.buildPrices.findFirstOrThrow({ - where: { - houseType: { equals: input.houseType }, - }, - select: { priceMid: true }, - }); - // TODO: Make columns non-nullable - if (!buildPrice) throw Error("Missing buildPrice"); - - const itl3 = await itl3Service.getByPostcodeDistrict(postcodeDistrict); - const gdhi = await ghdiService.getByITL3(itl3); - - const { - _avg: { monthlyMeanRent: averageRentMonthly }, - } = await prisma.rent.aggregate({ - where: { itl3 }, - _avg: { - monthlyMeanRent: true, - }, - }); - if (!averageRentMonthly) throw Error("Missing averageRentMonthly"); - - const socialRentAdjustments = await prisma.socialRentAdjustments.findMany(); - const itl3Prefix = itl3.substring(0, 4); - - const { - _avg: { earningsPerWeek: socialRentAveEarning }, - } = await prisma.socialRent.aggregate({ - where: { - itl3: { - startsWith: itl3Prefix, - }, - }, - _avg: { - earningsPerWeek: true, - }, - }); - - if (!socialRentAveEarning) throw Error("Missing socialRentAveEarning"); - - const { - _avg: { hpi2020: averageHpi }, - } = await prisma.hPI.aggregate({ - where: { - itl3: { - endsWith: itl3, - }, - }, - _avg: { - hpi2020: true, - }, - }); - if (!averageHpi) throw Error("Missing averageHpi"); - - const { bill: gasBillYearly } = await prisma.gasBills.findFirstOrThrow({ - where: { - itl: { - startsWith: itl3.substring(0, 3), - }, - }, - select: { - bill: true, - }, - }); - if (!gasBillYearly) throw Error("Missing gasBillYearly"); - - return NextResponse.json({ - postcode: input.housePostcode, - houseType: input.houseType, - houseAge: input.houseAge, - houseBedrooms: input.houseBedrooms, - houseSize: input.houseSize, - averagePrice: parseFloat(averagePrice.toFixed(2)), - itl3, - gdhi, - hpi: averageHpi, - buildPrice, - averageRentMonthly, - socialRentAdjustments, - socialRentAveEarning, - numberOfTransactions, - granularityPostcode, - pricesPaid, - gasBillYearly, - }); + const householdData = await calculationService.getHouseholdData(input); + return NextResponse.json(householdData); } catch (err) { console.log("ERROR: API - ", (err as Error).message); const response = { error: (err as Error).message }; return NextResponse.json(response, { status: 500 }); - } finally { - await prisma.$disconnect(); } } diff --git a/app/services/calculationService.ts b/app/services/calculationService.ts new file mode 100644 index 0000000..2e47df8 --- /dev/null +++ b/app/services/calculationService.ts @@ -0,0 +1,198 @@ +import { PrismaClient } from "@prisma/client"; +import * as itl3Service from "../services/itlLookupService"; +import * as ghdiService from "../services/gdhiService"; +import { Calculation } from "../schemas/calculationSchema"; + +const prisma = new PrismaClient(); + +export const getHouseholdData = async ( + input: Calculation +) => { + try { + // data are going to be queried at different levels of granularity based on the postcode + const postcode = input.housePostcode; + const postcodeArea = postcode.area; // extract only the characters for the area, e.g SE + const postcodeDistrict = postcode.district; // extract only characters for the district, SE17 + const postcodeSector = postcode.sector; // extract only the characters for the sector, SE17 1 + + // create the progressive queries + const minimumNumberPostcodes = 30; // minimum number of entries to create the average + let pricesPaid; // declare the variable for prices paid + let numberOfTransactions; // declare the variable for numbers of transactions retrieved + let granularityPostcode; // declare the granularity of the postcode + let averagePrice; + + const pricesPaidSector = await prisma.pricesPaid.aggregate({ + where: { + propertyType: { + equals: input.houseType, + }, + postcode: { + startsWith: postcodeSector, + }, + }, + _count: { + id: true, + }, + _avg: { + price: true, + }, + }); + + const numberPerSector = pricesPaidSector._count.id; + const isMinMetBySector = numberPerSector >= minimumNumberPostcodes; + + if (!isMinMetBySector) { + const pricesPaidDistrict = await prisma.pricesPaid.aggregate({ + where: { + propertyType: { + equals: input.houseType, + }, + postcode: { + startsWith: postcodeDistrict, + }, + }, + _count: { + id: true, + }, + _avg: { + price: true, + }, + }); + + const numberPerDistrict = pricesPaidDistrict._count.id; + const isMinMetByDistrict = numberPerDistrict >= minimumNumberPostcodes; + + if (!isMinMetByDistrict) { + const pricesPaidArea = await prisma.pricesPaid.aggregate({ + where: { + propertyType: { + equals: input.houseType, + }, + postcode: { + startsWith: postcodeArea, + }, + }, + _count: { + id: true, + }, + _avg: { + price: true, + }, + }); + const numberPerArea = pricesPaidArea._count.id; + + pricesPaid = pricesPaidArea; // if condition is met, the granularity is appropriate + numberOfTransactions = numberPerArea; // check the granularity + granularityPostcode = postcodeArea; // granularity of the postcode when performing the average price search + averagePrice = pricesPaidArea._avg.price; + } else { + pricesPaid = pricesPaidDistrict; // if condition is met, the granularity is appropriate + numberOfTransactions = numberPerDistrict; // check the granularity + granularityPostcode = postcodeDistrict; // granularity of the postcode + averagePrice = pricesPaidDistrict._avg.price; + } + } else { + pricesPaid = pricesPaidSector; // if condition is met, the granularity is appropriate + numberOfTransactions = numberPerSector; // check the granularity + granularityPostcode = postcodeSector; // granularity of the postcode + averagePrice = pricesPaidSector._avg.price; + } + + if (averagePrice === null) { + throw new Error("Unable to calculate average price"); + } + + const { priceMid: buildPrice } = await prisma.buildPrices.findFirstOrThrow({ + where: { + houseType: { equals: input.houseType }, + }, + select: { priceMid: true }, + }); + // TODO: Make columns non-nullable + if (!buildPrice) throw Error("Missing buildPrice"); + + const itl3 = await itl3Service.getByPostcodeDistrict(postcodeDistrict); + const gdhi = await ghdiService.getByITL3(itl3); + + const { + _avg: { monthlyMeanRent: averageRentMonthly }, + } = await prisma.rent.aggregate({ + where: { itl3 }, + _avg: { + monthlyMeanRent: true, + }, + }); + if (!averageRentMonthly) throw Error("Missing averageRentMonthly"); + + const socialRentAdjustments = await prisma.socialRentAdjustments.findMany(); + const itl3Prefix = itl3.substring(0, 4); + + const { + _avg: { earningsPerWeek: socialRentAveEarning }, + } = await prisma.socialRent.aggregate({ + where: { + itl3: { + startsWith: itl3Prefix, + }, + }, + _avg: { + earningsPerWeek: true, + }, + }); + + if (!socialRentAveEarning) throw Error("Missing socialRentAveEarning"); + + const { + _avg: { hpi2020: averageHpi }, + } = await prisma.hPI.aggregate({ + where: { + itl3: { + endsWith: itl3, + }, + }, + _avg: { + hpi2020: true, + }, + }); + if (!averageHpi) throw Error("Missing averageHpi"); + + const { bill: gasBillYearly } = await prisma.gasBills.findFirstOrThrow({ + where: { + itl: { + startsWith: itl3.substring(0, 3), + }, + }, + select: { + bill: true, + }, + }); + if (!gasBillYearly) throw Error("Missing gasBillYearly"); + + return { + postcode: input.housePostcode, + houseType: input.houseType, + houseAge: input.houseAge, + houseBedrooms: input.houseBedrooms, + houseSize: input.houseSize, + averagePrice: parseFloat(averagePrice.toFixed(2)), + itl3, + gdhi, + hpi: averageHpi, + buildPrice, + averageRentMonthly, + socialRentAdjustments, + socialRentAveEarning, + numberOfTransactions, + granularityPostcode, + pricesPaid, + gasBillYearly, + } + } catch (err) { + throw Error( + `Service error: Unable to generate household. Message: ${(err as Error).message}` + ); + } finally { + await prisma.$disconnect(); + } +}; From 0436b0cbfd41cf15aa239515673210d547035b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Sat, 3 Aug 2024 12:21:35 +0100 Subject: [PATCH 7/7] chore: Tidy up import, export and names --- app/data/gdhiRepo.ts | 6 +++++- app/data/{itlLookupRepo.ts => itlRepo.ts} | 6 +++++- app/services/calculationService.ts | 8 ++++---- app/services/gdhiService.ts | 10 +++++++--- app/services/itlLookupService.ts | 5 ----- app/services/itlService.ts | 9 +++++++++ 6 files changed, 30 insertions(+), 14 deletions(-) rename app/data/{itlLookupRepo.ts => itlRepo.ts} (84%) delete mode 100644 app/services/itlLookupService.ts create mode 100644 app/services/itlService.ts diff --git a/app/data/gdhiRepo.ts b/app/data/gdhiRepo.ts index 0c649f0..69ed3b8 100644 --- a/app/data/gdhiRepo.ts +++ b/app/data/gdhiRepo.ts @@ -1,6 +1,6 @@ import prisma from "./db"; -export const getGDHI2020ByITL3 = async ( +const getGDHI2020ByITL3 = async ( itl3: string ): Promise => { try { @@ -20,3 +20,7 @@ export const getGDHI2020ByITL3 = async ( throw Error(`Data error: Unable to find gdhi2020 for itl3 ${itl3}`); } }; + +export const gdhiRepo = { + getGDHI2020ByITL3, +} diff --git a/app/data/itlLookupRepo.ts b/app/data/itlRepo.ts similarity index 84% rename from app/data/itlLookupRepo.ts rename to app/data/itlRepo.ts index d72e518..8bc92b4 100644 --- a/app/data/itlLookupRepo.ts +++ b/app/data/itlRepo.ts @@ -1,6 +1,6 @@ import prisma from "./db"; -export const getItl3ByPostcodeDistrict = async ( +const getItl3ByPostcodeDistrict = async ( postcodeDistrict: string ): Promise => { try { @@ -22,3 +22,7 @@ export const getItl3ByPostcodeDistrict = async ( throw new Error(`Data error: Unable get get itl3 for postcode district ${postcodeDistrict}`); } }; + +export const itlRepo = { + getItl3ByPostcodeDistrict, +} diff --git a/app/services/calculationService.ts b/app/services/calculationService.ts index 2e47df8..50d30de 100644 --- a/app/services/calculationService.ts +++ b/app/services/calculationService.ts @@ -1,6 +1,6 @@ import { PrismaClient } from "@prisma/client"; -import * as itl3Service from "../services/itlLookupService"; -import * as ghdiService from "../services/gdhiService"; +import { itlService } from "./itlService"; +import { gdhiService } from "./gdhiService"; import { Calculation } from "../schemas/calculationSchema"; const prisma = new PrismaClient(); @@ -112,8 +112,8 @@ export const getHouseholdData = async ( // TODO: Make columns non-nullable if (!buildPrice) throw Error("Missing buildPrice"); - const itl3 = await itl3Service.getByPostcodeDistrict(postcodeDistrict); - const gdhi = await ghdiService.getByITL3(itl3); + const itl3 = await itlService.getByPostcodeDistrict(postcodeDistrict); + const gdhi = await gdhiService.getByITL3(itl3); const { _avg: { monthlyMeanRent: averageRentMonthly }, diff --git a/app/services/gdhiService.ts b/app/services/gdhiService.ts index dccbb16..3dbb752 100644 --- a/app/services/gdhiService.ts +++ b/app/services/gdhiService.ts @@ -1,5 +1,9 @@ -import * as repo from './../data/gdhiRepo'; +import { gdhiRepo } from "../data/gdhiRepo"; -export const getByITL3 = async (itl3: string) => { - return await repo.getGDHI2020ByITL3(itl3); +const getByITL3 = async (itl3: string) => { + return await gdhiRepo.getGDHI2020ByITL3(itl3); +}; + +export const gdhiService = { + getByITL3, }; diff --git a/app/services/itlLookupService.ts b/app/services/itlLookupService.ts deleted file mode 100644 index a04a2f1..0000000 --- a/app/services/itlLookupService.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as repo from "../data/itlLookupRepo"; - -export const getByPostcodeDistrict = async (postcodeDistrict: string) => { - return await repo.getItl3ByPostcodeDistrict(postcodeDistrict); -} diff --git a/app/services/itlService.ts b/app/services/itlService.ts new file mode 100644 index 0000000..358af0f --- /dev/null +++ b/app/services/itlService.ts @@ -0,0 +1,9 @@ +import { itlRepo } from "../data/itlRepo"; + +const getByPostcodeDistrict = async (postcodeDistrict: string) => { + return await itlRepo.getItl3ByPostcodeDistrict(postcodeDistrict); +} + +export const itlService = { + getByPostcodeDistrict, +}