Throw on wrong-field-name parse failures instead of silent zero-point success
Spot is now confirmed subscribed to hourly/three-hourly too (settings' met_office test matrix), so those parsers are genuinely exercised for the first time -- previously a wrong field name would silently produce an empty points array while still reporting the tier as a 'met_office' success. assertParsed() throws when raw entries came back but none parsed a valid temp_c, so a field-name mismatch now correctly falls back to Open-Meteo (and logs an error) instead of quietly showing an empty today strip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
15a801b1f4
commit
64afd2b5fd
1 changed files with 22 additions and 7 deletions
|
|
@ -62,12 +62,26 @@ async function fetchMetOfficeTimestep(timestep, lat, lon, apiKey) {
|
||||||
return series
|
return series
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throws if the raw response had entries but every one of them failed to parse a
|
||||||
|
// valid temp_c — that's not "no data for this range" (which is a legitimate empty
|
||||||
|
// array, not an error), it's "the field names are wrong". Confirmed live 2026-08-25
|
||||||
|
// that Spot is actually subscribed to all three endpoints (settings' met_office
|
||||||
|
// test route), so hourly/three-hourly parsing is now genuinely exercised for the
|
||||||
|
// first time — this turns a silent wrong-field-name bug (reported as "success"
|
||||||
|
// with zero points) into a real failure that falls back to Open-Meteo instead.
|
||||||
|
function assertParsed(rawCount, points, label) {
|
||||||
|
if (rawCount > 0 && points.length === 0) {
|
||||||
|
throw new Error(`Met Office ${label}: got ${rawCount} raw entries but parsed 0 valid points — check field names`)
|
||||||
|
}
|
||||||
|
return points
|
||||||
|
}
|
||||||
|
|
||||||
// Every hour the endpoint returns, not just today — the "tap a Coming up card"
|
// Every hour the endpoint returns, not just today — the "tap a Coming up card"
|
||||||
// hourly-detail modal needs this for tomorrow/day-after (and, where available,
|
// hourly-detail modal needs this for tomorrow/day-after (and, where available,
|
||||||
// days 4-7) too.
|
// days 4-7) too.
|
||||||
async function fetchMetOfficeHourly(lat, lon, apiKey) {
|
async function fetchMetOfficeHourly(lat, lon, apiKey) {
|
||||||
const series = await fetchMetOfficeTimestep('hourly', lat, lon, apiKey)
|
const series = await fetchMetOfficeTimestep('hourly', lat, lon, apiKey)
|
||||||
return series
|
const points = series
|
||||||
.map(p => ({
|
.map(p => ({
|
||||||
resolution: 'hourly',
|
resolution: 'hourly',
|
||||||
ts: p.time,
|
ts: p.time,
|
||||||
|
|
@ -81,12 +95,13 @@ async function fetchMetOfficeHourly(lat, lon, apiKey) {
|
||||||
source: 'met_office',
|
source: 'met_office',
|
||||||
}))
|
}))
|
||||||
.filter(p => typeof p.temp_c === 'number')
|
.filter(p => typeof p.temp_c === 'number')
|
||||||
|
return assertParsed(series.length, points, 'hourly')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchMetOfficeThreeHourly(lat, lon, apiKey, dayKeys) {
|
async function fetchMetOfficeThreeHourly(lat, lon, apiKey, dayKeys) {
|
||||||
const series = await fetchMetOfficeTimestep('three-hourly', lat, lon, apiKey)
|
const series = await fetchMetOfficeTimestep('three-hourly', lat, lon, apiKey)
|
||||||
return series
|
const dayRows = series.filter(p => dayKeys.includes(londonDateKey(p.time)))
|
||||||
.filter(p => dayKeys.includes(londonDateKey(p.time)))
|
const points = dayRows
|
||||||
.map(p => ({
|
.map(p => ({
|
||||||
resolution: '3hourly',
|
resolution: '3hourly',
|
||||||
ts: p.time,
|
ts: p.time,
|
||||||
|
|
@ -100,6 +115,7 @@ async function fetchMetOfficeThreeHourly(lat, lon, apiKey, dayKeys) {
|
||||||
source: 'met_office',
|
source: 'met_office',
|
||||||
}))
|
}))
|
||||||
.filter(p => typeof p.temp_c === 'number')
|
.filter(p => typeof p.temp_c === 'number')
|
||||||
|
return assertParsed(dayRows.length, points, 'three-hourly')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns both the daily tier's points AND astro (sunrise/sunset) — the daily
|
// Returns both the daily tier's points AND astro (sunrise/sunset) — the daily
|
||||||
|
|
@ -108,9 +124,8 @@ async function fetchMetOfficeThreeHourly(lat, lon, apiKey, dayKeys) {
|
||||||
// openMeteoAstroPoints below).
|
// openMeteoAstroPoints below).
|
||||||
async function fetchMetOfficeDaily(lat, lon, apiKey, skipKeys, astroKeys) {
|
async function fetchMetOfficeDaily(lat, lon, apiKey, skipKeys, astroKeys) {
|
||||||
const series = await fetchMetOfficeTimestep('daily', lat, lon, apiKey)
|
const series = await fetchMetOfficeTimestep('daily', lat, lon, apiKey)
|
||||||
const dailyPoints = series
|
const dayRows = series.filter(p => !skipKeys.has(londonDateKey(p.time))).slice(0, 4)
|
||||||
.filter(p => !skipKeys.has(londonDateKey(p.time)))
|
const dailyPoints = assertParsed(dayRows.length, dayRows
|
||||||
.slice(0, 4)
|
|
||||||
.map(p => ({
|
.map(p => ({
|
||||||
resolution: 'daily',
|
resolution: 'daily',
|
||||||
ts: p.time,
|
ts: p.time,
|
||||||
|
|
@ -123,7 +138,7 @@ async function fetchMetOfficeDaily(lat, lon, apiKey, skipKeys, astroKeys) {
|
||||||
icon: iconFromMetOffice(p.daySignificantWeatherCode),
|
icon: iconFromMetOffice(p.daySignificantWeatherCode),
|
||||||
source: 'met_office',
|
source: 'met_office',
|
||||||
}))
|
}))
|
||||||
.filter(p => typeof p.temp_c === 'number')
|
.filter(p => typeof p.temp_c === 'number'), 'daily')
|
||||||
const astroPoints = series
|
const astroPoints = series
|
||||||
.filter(p => astroKeys.includes(londonDateKey(p.time)))
|
.filter(p => astroKeys.includes(londonDateKey(p.time)))
|
||||||
.map(p => ({ date: londonDateKey(p.time), sunrise: p.sunrise ?? null, sunset: p.sunset ?? null }))
|
.map(p => ({ date: londonDateKey(p.time), sunrise: p.sunrise ?? null, sunset: p.sunset ?? null }))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue