diff --git a/src/index.html b/src/index.html index 641c08a..614727e 100644 --- a/src/index.html +++ b/src/index.html @@ -136,14 +136,14 @@

Test

diff --git a/src/index.test.ts b/src/index.test.ts index ebfd892..babc316 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,5 +1,5 @@ -import { ACCUWEATHER_STRUCT, FORECA_STRUCT, SIMPLE_STRUCT } from './structs.ts' import { QueryParams } from './types.ts' +import { STRUCTS } from './structs.ts' import main from './index.ts' type OptionalParams = Partial> @@ -17,7 +17,7 @@ Deno.test('Provider: Accuweather', async function () { lat: LAT, lon: LON, }), - FORECA_STRUCT, + STRUCTS.FORECA.WEATHER, ) }) @@ -28,7 +28,7 @@ Deno.test('Provider: Foreca', async function () { lat: LAT, lon: LON, }), - FORECA_STRUCT, + STRUCTS.FORECA.WEATHER, ) }) @@ -39,7 +39,7 @@ Deno.test('Provider: Auto', async function () { lat: LAT, lon: LON, }), - SIMPLE_STRUCT, + STRUCTS.SIMPLE.WEATHER, ) }) @@ -50,7 +50,7 @@ Deno.test.ignore('Provider: Wunderground', async function () { lat: LAT, lon: LON, }), - ACCUWEATHER_STRUCT, + STRUCTS.ACCUWEATHER.WEATHER, ) }) @@ -64,7 +64,7 @@ Deno.test('Simple data: Accuweather', async function () { lat: LAT, lon: LON, }), - SIMPLE_STRUCT, + STRUCTS.SIMPLE.WEATHER, ) }) @@ -76,7 +76,7 @@ Deno.test('Simple data: Foreca', async function () { lat: LAT, lon: LON, }), - SIMPLE_STRUCT, + STRUCTS.SIMPLE.WEATHER, ) }) @@ -88,7 +88,43 @@ Deno.test('Simple data: Auto overrides "all" data', async function () { lat: LAT, lon: LON, }), - SIMPLE_STRUCT, + STRUCTS.SIMPLE.WEATHER, + ) +}) + +Deno.test.ignore('Geo: Accuweather', async function () { + compareTypes( + await getJson({ + provider: 'accuweather', + geo: 'true', + lat: LAT, + lon: LON, + }), + STRUCTS.ACCUWEATHER.GEO, + ) +}) + +Deno.test.ignore('Geo: Foreca', async function () { + compareTypes( + await getJson({ + provider: 'accuweather', + geo: 'true', + lat: LAT, + lon: LON, + }), + STRUCTS.ACCUWEATHER.GEO, + ) +}) + +Deno.test.ignore('Geo: Simple', async function () { + compareTypes( + await getJson({ + provider: 'accuweather', + geo: 'true', + lat: LAT, + lon: LON, + }), + STRUCTS.ACCUWEATHER.GEO, ) }) @@ -129,8 +165,8 @@ Deno.test('Params: Wrong unit falls back to C', async function () { Deno.test('Params: Has provider & no query/coords', async function () { const req = new Request(`https://example.com?provider=accuweather`) const resp = await main.fetch(req) - assert(resp.status === 503) + console.clear() }) // Helpers diff --git a/src/index.ts b/src/index.ts index 378abc0..f53267f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,10 +2,10 @@ import './parser.ts' import * as foreca from './providers/foreca.ts' import * as weathercom from './providers/weathercom.ts' import * as accuweather from './providers/accuweather.ts' -import toSimpleWeather from './providers/simple.ts' -import { isAccuweather, isForeca } from './types.ts' +import toSimpleWeather, { toSimpleLocations } from './providers/simple.ts' +import { isAccuweather, isAccuweatherLocation, isForeca, isForecaLocation } from './types.ts' -import type { AccuWeather, Foreca, QueryParams } from './types.ts' +import type { AccuWeather, Foreca, QueryParams, Simple } from './types.ts' /** * Racle-météo can be called like a Cloudflare Worker, using fetch(). @@ -84,16 +84,21 @@ async function main(request: Request) { } if (params.geo && params.provider) { - let list: AccuWeather.Locations | Foreca.Location = [] + let list: unknown[] = [] - if (params.provider === 'accuweather') { + if (params.provider === 'accuweather' || params.provider === 'auto') { list = await accuweather.geo(params) } - - if (params.provider === 'foreca') { + if (params.provider === 'foreca' || (params.provider === 'auto' && list.length === 0)) { list = await foreca.geo(params) } + if (params.provider === 'auto' || params.data === 'simple') { + if (isAccuweatherLocation(list) || isForecaLocation(list)) { + list = toSimpleLocations(list) + } + } + return new Response(JSON.stringify(list), { status, headers: { diff --git a/src/providers/accuweather.ts b/src/providers/accuweather.ts index c49c129..2e0b2ce 100644 --- a/src/providers/accuweather.ts +++ b/src/providers/accuweather.ts @@ -16,7 +16,7 @@ export default async function accuweather(params: QueryParams): Promise { +export async function geo(params: QueryParams): Promise { return await geolocationFromQuery(params.query) } @@ -215,7 +215,7 @@ async function fetchPageContent(params: QueryParams): Promise { return html } -async function geolocationFromQuery(query: string): Promise { +async function geolocationFromQuery(query: string): Promise { const headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0', Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', @@ -226,7 +226,7 @@ async function geolocationFromQuery(query: string): Promise 1) { return result diff --git a/src/providers/foreca.ts b/src/providers/foreca.ts index 5d5ae3e..25d90f8 100644 --- a/src/providers/foreca.ts +++ b/src/providers/foreca.ts @@ -19,12 +19,14 @@ export default async function foreca(params: QueryParams): Promise { - return await getForecaLocation({ - lat: params.lat, - lon: params.lon, - query: params.query, - }) +export async function geo(params: QueryParams): Promise { + return [ + await getForecaLocation({ + lat: params.lat, + lon: params.lon, + query: params.query, + }), + ] } export async function debugContent(params: QueryParams): Promise { @@ -213,11 +215,18 @@ export async function fetchPageContent({ lat, lon, query, lang, unit }: QueryPar } export async function getForecaLocation({ lat, lon, query }: Partial): Promise { + type LocationSearch = { + query: string + language: string + results: Foreca.Location[] + } + if (query) { const path = `https://api.foreca.net/locations/search/${query}.json` const resp = await fetch(path) - const json = await resp.json() - return json.results[0] + const json = await resp.json() as LocationSearch + const location = json.results[0] + return location } else { const path = `https://api.foreca.net/locations/${lon},${lat}.json` const resp = await fetch(path) @@ -226,8 +235,8 @@ export async function getForecaLocation({ lat, lon, query }: Partial ({ name: loc.name, detail: loc.longName })) + } + + if (isForecaLocation(json)) { + return json.map((loc) => ({ name: loc.name, detail: loc.defaultName })) + } + + throw new Error('Cannot get a valid location') +} + function transformToSimpleIcon(id: string, provider: 'accuweather' | 'foreca'): string { for (const [simpleId, providerIds] of Object.entries(SIMPLE_ICONS)) { const list = providerIds[provider] diff --git a/src/structs.ts b/src/structs.ts index f15629b..d66bd9b 100644 --- a/src/structs.ts +++ b/src/structs.ts @@ -1,4 +1,4 @@ -export const SIMPLE_STRUCT = { +const SIMPLE_STRUCT = { meta: { url: 'string', lang: 'string', @@ -29,7 +29,14 @@ export const SIMPLE_STRUCT = { ], } -export const ACCUWEATHER_STRUCT = { +const SIMPLE_GEO_STRUCT = [ + { + name: 'string', + details: 'string', + }, +] + +const ACCUWEATHER_STRUCT = { meta: { url: 'string', lang: 'string', @@ -69,7 +76,14 @@ export const ACCUWEATHER_STRUCT = { ], } -export const FORECA_STRUCT = { +const ACCUWEATHER_GEO_STRUCT = [ + { + name: 'string', + details: 'string', + }, +] + +const FORECA_STRUCT = { meta: { url: 'string', lang: 'string', @@ -102,3 +116,25 @@ export const FORECA_STRUCT = { }, ], } + +const FORECA_GEO_STRUCT = [ + { + name: 'string', + details: 'string', + }, +] + +export const STRUCTS = { + ACCUWEATHER: { + WEATHER: ACCUWEATHER_STRUCT, + GEO: ACCUWEATHER_GEO_STRUCT, + }, + FORECA: { + WEATHER: FORECA_STRUCT, + GEO: FORECA_GEO_STRUCT, + }, + SIMPLE: { + WEATHER: SIMPLE_STRUCT, + GEO: SIMPLE_GEO_STRUCT, + }, +} diff --git a/src/types.ts b/src/types.ts index ac53d1f..1e9780e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,6 +6,14 @@ export function isForeca(json: AccuWeather.Weather | Foreca.Weather): json is Fo return json?.meta?.provider === 'foreca' } +export function isAccuweatherLocation(json: unknown[]): json is AccuWeather.Location[] { + return !!(json[0] as AccuWeather.Location)?.key +} + +export function isForecaLocation(json: unknown[]): json is Foreca.Location[] { + return !!(json[0] as Foreca.Location)?.id +} + export interface QueryParams { provider: 'accuweather' | 'foreca' | 'weathercom' | 'auto' | '' debug: 'nodes' | 'content' | 'geo' | '' @@ -18,10 +26,6 @@ export interface QueryParams { geo?: unknown } -/************* - Simple -**************/ - export declare namespace Simple { interface Weather { meta: { @@ -54,14 +58,10 @@ export declare namespace Simple { type Locations = { name: string - longName: string + detail: string }[] } -/***************** - Accuweather -******************/ - export declare namespace AccuWeather { interface Weather { meta: { @@ -127,7 +127,7 @@ export declare namespace AccuWeather { }[] } - type Locations = { + interface Location { 'key': string 'name': string 'longName': string @@ -150,13 +150,9 @@ export declare namespace AccuWeather { 'country': string | null 'localizedName': string | null 'primaryPostalCode': string | null - }[] + } } -/************* - Foreca -**************/ - export declare namespace Foreca { interface Weather { meta: {