perf(decode): slim decode response to id + display summary
POST /vehicles/decode returned the full vehicles row while every consumer
(search, landing, service-test) only navigates by id and re-fetches via
GET /vehicles/:id. That shipped the raw upstream decode payload to the
client on every decode — rawData is 19KB avg / 127KB p95 / 207KB max in
prod — plus the provider name, for nothing.
The response is now { id, brandName, model, year } on both the DB-hit and
fresh-decode paths. Frontend drops the (now absent) source property from
the vin_decode_success event; decode source remains queryable server-side
via query_logs.source.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -166,7 +166,11 @@ describe("VehiclesService", () => {
|
||||
|
||||
const { service } = createService(db);
|
||||
const result = await service.decodeVin("WBAPH5C55BA123456", "u1");
|
||||
expect(result).toEqual(cached);
|
||||
// Decode response is intentionally slim — id + display summary only;
|
||||
// rawData/source never leave the API on this endpoint.
|
||||
expect(result).toMatchObject({ id: "v1" });
|
||||
expect(result).not.toHaveProperty("rawData");
|
||||
expect(result).not.toHaveProperty("source");
|
||||
});
|
||||
|
||||
it("should throw BadRequestException when corgi doesn't recognize VIN", async () => {
|
||||
@@ -435,7 +439,8 @@ describe("VehiclesService", () => {
|
||||
});
|
||||
|
||||
const result = await service.decodeVin("WBAPH5C55BA123456", "u1");
|
||||
expect(result).toEqual(savedVehicle);
|
||||
expect(result).toMatchObject({ id: "v-emex" });
|
||||
expect(result).not.toHaveProperty("source");
|
||||
expect(partsCatalogsService.decodeVin).toHaveBeenCalled();
|
||||
expect(emexService.decodeVinOrCandidates).toHaveBeenCalledWith("WBAPH5C55BA123456");
|
||||
});
|
||||
@@ -473,7 +478,7 @@ describe("VehiclesService", () => {
|
||||
emexService.decodeVinOrCandidates.mockResolvedValue({ type: "notFound" });
|
||||
|
||||
const result = await service.decodeVin("WVWZZZ1JZ3W597935", "u1");
|
||||
expect(result).toEqual(savedVehicle);
|
||||
expect(result).toMatchObject({ id: "v-pcat" });
|
||||
expect(pl24Service.decodeVin).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -488,7 +493,7 @@ describe("VehiclesService", () => {
|
||||
});
|
||||
|
||||
const result = await service.decodeVin("WBAPH5C55BA123456", "u1");
|
||||
expect(result).toEqual(savedVehicle);
|
||||
expect(result).toMatchObject({ id: "v-emex" });
|
||||
expect(emexService.decodeVinOrCandidates).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -511,7 +516,7 @@ describe("VehiclesService", () => {
|
||||
});
|
||||
|
||||
const result = await service.decodeVin("VR3EFYHZ3PJ674071", "u1");
|
||||
expect(result).toEqual(savedVehicle);
|
||||
expect(result).toMatchObject({ id: "v-pl24" });
|
||||
expect(pl24Service.decodeVin).toHaveBeenCalledWith("VR3EFYHZ3PJ674071", "u1");
|
||||
});
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ export class VehiclesService {
|
||||
undefined,
|
||||
ctx.timings,
|
||||
);
|
||||
return existing;
|
||||
return this.toDecodeResponse(existing);
|
||||
}
|
||||
|
||||
// 2. Resolve VIN via cached decode chain (Corgi → PartsCatalogs → PL24 → EMEX)
|
||||
@@ -308,7 +308,22 @@ export class VehiclesService {
|
||||
ctx.timings,
|
||||
);
|
||||
|
||||
return savedVehicle;
|
||||
return this.toDecodeResponse(savedVehicle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal decode response. Every consumer navigates by id and re-fetches the
|
||||
* vehicle via GET /vehicles/:id; returning the full row shipped the raw
|
||||
* upstream decode payload (rawData, p95 ~127KB) and the provider name to the
|
||||
* client on every decode for nothing.
|
||||
*/
|
||||
private toDecodeResponse(v: {
|
||||
id: string;
|
||||
brandName: string | null;
|
||||
model: string | null;
|
||||
year: number | null;
|
||||
}) {
|
||||
return { id: v.id, brandName: v.brandName, model: v.model, year: v.year };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user