mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-12 13:28:55 +00:00
fix(property-meta): resolve in-process, drop self-fetch to process.env.URL
getPropertyMeta self-fetched process.env.URL + /api/property-meta. That URL can resolve to a different deployment (e.g. production), so the building-passport pages were reading property meta from OLD code — the backfilled headline SAP/EPC never arrived and the current rating rendered blank, regardless of caching. Extract resolvePropertyMeta() and call it directly from both the route and getPropertyMeta, so the page runs identical in-process logic. JSON round-trip through serializeBigInt preserves the previous shape (bigints/dates as strings). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e1d56cc773
commit
fec714a406
3 changed files with 69 additions and 66 deletions
|
|
@ -1,66 +1,20 @@
|
|||
import { db } from "@/app/db/db";
|
||||
import { property } from "@/app/db/schema/property";
|
||||
import { serializeBigInt } from "@/app/utils";
|
||||
import {
|
||||
resolveDetailsEpcMeta,
|
||||
resolvePropertyHeadline,
|
||||
} from "@/lib/services/epcSources";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { resolvePropertyMeta } from "@/lib/services/epcSources";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function GET(request: NextRequest, props: { params: Promise<{ propertyId: string }> }) {
|
||||
const params = await props.params;
|
||||
const propertyId = params.propertyId;
|
||||
|
||||
const propertyMeta = await db.query.property.findFirst({
|
||||
columns: {
|
||||
id: true,
|
||||
uprn: true,
|
||||
address: true,
|
||||
postcode: true,
|
||||
hasPreConditionReport: true,
|
||||
hasRecommendations: true,
|
||||
createdAt: true,
|
||||
propertyType: true,
|
||||
builtForm: true,
|
||||
localAuthority: true,
|
||||
constituency: true,
|
||||
numberOfRooms: true,
|
||||
yearBuilt: true,
|
||||
tenure: true,
|
||||
currentEpcRating: true,
|
||||
currentSapPoints: true,
|
||||
originalSapPoints: true,
|
||||
updatedAt: true,
|
||||
currentValuation: true,
|
||||
},
|
||||
where: eq(property.id, BigInt(propertyId)),
|
||||
});
|
||||
// `detailsEpc` and the headline SAP/EPC are resolved via the cutoff: legacy
|
||||
// properties read property_details_epc / the property row; new-approach ones
|
||||
// read property_baseline_performance (the property-row headline columns aren't
|
||||
// written yet). See src/lib/services/epcSources.ts.
|
||||
const propertyMeta = await resolvePropertyMeta(propertyId);
|
||||
|
||||
if (!propertyMeta) {
|
||||
return new NextResponse(null, { status: 404 });
|
||||
}
|
||||
|
||||
// `detailsEpc` and the headline SAP/EPC are resolved via the cutoff: legacy
|
||||
// properties read property_details_epc / the property row; new-approach ones
|
||||
// read property_baseline_performance (the property-row headline columns aren't
|
||||
// written yet). See src/lib/services/epcSources.ts.
|
||||
const [detailsEpc, headline] = await Promise.all([
|
||||
resolveDetailsEpcMeta(propertyMeta.id, propertyMeta.updatedAt),
|
||||
resolvePropertyHeadline(propertyMeta.id, propertyMeta.updatedAt),
|
||||
]);
|
||||
|
||||
return new NextResponse(
|
||||
JSON.stringify(
|
||||
{
|
||||
...propertyMeta,
|
||||
currentSapPoints:
|
||||
headline.currentSapPoints ?? propertyMeta.currentSapPoints,
|
||||
currentEpcRating:
|
||||
headline.currentEpcRating ?? propertyMeta.currentEpcRating,
|
||||
detailsEpc,
|
||||
},
|
||||
serializeBigInt,
|
||||
),
|
||||
);
|
||||
return new NextResponse(JSON.stringify(propertyMeta, serializeBigInt));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ import {
|
|||
import {
|
||||
ConditionReport,
|
||||
resolveConditionReport,
|
||||
resolvePropertyMeta,
|
||||
} from "@/lib/services/epcSources";
|
||||
import { getRating } from "@/app/utils";
|
||||
import { getRating, serializeBigInt } from "@/app/utils";
|
||||
import { eq, desc, and } from "drizzle-orm";
|
||||
import {
|
||||
energyAssessment,
|
||||
|
|
@ -240,20 +241,17 @@ export async function getPlans(propertyId: string): Promise<PlanRelation[]> {
|
|||
export async function getPropertyMeta(
|
||||
propertyId: string
|
||||
): Promise<PropertyMeta> {
|
||||
const url = process.env.URL + `/api/property-meta/${propertyId}`;
|
||||
|
||||
// Don't cache: property meta (incl. headline SAP/EPC, which is now backfilled
|
||||
// from the baseline table) changes as the modelling pipeline writes data, and
|
||||
// a stale Data Cache entry was causing the current rating to render blank.
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
cache: "no-store",
|
||||
});
|
||||
if (!response.ok) {
|
||||
// Resolve in-process instead of self-fetching /api/property-meta. The HTTP
|
||||
// round-trip went to process.env.URL, which can point at a different
|
||||
// deployment (e.g. production) and was serving stale/old-code responses — so
|
||||
// the backfilled headline SAP/EPC never reached the page. We JSON round-trip
|
||||
// through serializeBigInt to keep the exact shape the fetch used to return
|
||||
// (bigints as strings, dates as ISO strings).
|
||||
const meta = await resolvePropertyMeta(propertyId);
|
||||
if (!meta) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
return response.json();
|
||||
return JSON.parse(JSON.stringify(meta, serializeBigInt)) as PropertyMeta;
|
||||
}
|
||||
|
||||
export async function getConditionReport(
|
||||
|
|
|
|||
|
|
@ -464,3 +464,54 @@ export async function resolveDetailsEpcMeta(
|
|||
estimated: legacy?.estimated ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the full property-meta object (the property row + branched
|
||||
* `detailsEpc` + headline SAP/EPC). Shared by the `/api/property-meta` route and
|
||||
* the building-passport `getPropertyMeta` so both run identical, in-process
|
||||
* logic — no self-fetch to `process.env.URL` (which could hit a different
|
||||
* deployment) and no Data Cache. Returns null when the property doesn't exist.
|
||||
* BigInt fields are left raw; the caller serialises with `serializeBigInt`.
|
||||
*/
|
||||
export async function resolvePropertyMeta(propertyId: string) {
|
||||
const propertyMeta = await db.query.property.findFirst({
|
||||
columns: {
|
||||
id: true,
|
||||
uprn: true,
|
||||
address: true,
|
||||
postcode: true,
|
||||
hasPreConditionReport: true,
|
||||
hasRecommendations: true,
|
||||
createdAt: true,
|
||||
propertyType: true,
|
||||
builtForm: true,
|
||||
localAuthority: true,
|
||||
constituency: true,
|
||||
numberOfRooms: true,
|
||||
yearBuilt: true,
|
||||
tenure: true,
|
||||
currentEpcRating: true,
|
||||
currentSapPoints: true,
|
||||
originalSapPoints: true,
|
||||
updatedAt: true,
|
||||
currentValuation: true,
|
||||
},
|
||||
where: eq(property.id, BigInt(propertyId)),
|
||||
});
|
||||
|
||||
if (!propertyMeta) return null;
|
||||
|
||||
const [detailsEpc, headline] = await Promise.all([
|
||||
resolveDetailsEpcMeta(propertyMeta.id, propertyMeta.updatedAt),
|
||||
resolvePropertyHeadline(propertyMeta.id, propertyMeta.updatedAt),
|
||||
]);
|
||||
|
||||
return {
|
||||
...propertyMeta,
|
||||
currentSapPoints:
|
||||
headline.currentSapPoints ?? propertyMeta.currentSapPoints,
|
||||
currentEpcRating:
|
||||
headline.currentEpcRating ?? propertyMeta.currentEpcRating,
|
||||
detailsEpc,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue