From 0e23e568172ec392b3fa34b131a20af0c3cabf7a Mon Sep 17 00:00:00 2001 From: Matthew Oslan Date: Tue, 21 May 2019 22:14:46 -0400 Subject: [PATCH] Add support for fallback WeatherProviders --- routes/weather.ts | 6 ++- .../CompositeWeatherProvider.ts | 45 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 routes/weatherProviders/CompositeWeatherProvider.ts diff --git a/routes/weather.ts b/routes/weather.ts index 2dbf7f6..dc15a5b 100644 --- a/routes/weather.ts +++ b/routes/weather.ts @@ -7,7 +7,11 @@ import * as geoTZ from "geo-tz"; import * as local from "./local"; import { AdjustmentOptions, GeoCoordinates, TimeData, WateringData, WeatherData, WeatherProvider } from "../types"; -const weatherProvider: WeatherProvider = require("./weatherProviders/" + ( process.env.WEATHER_PROVIDER || "OWM" ) ).default; +import CompositeWeatherProvider from "./weatherProviders/CompositeWeatherProvider"; + +const weatherProvider: WeatherProvider = new CompositeWeatherProvider( + ( process.env.WEATHER_PROVIDER || "OWM" ).split( "," ).map( ( id ) => require( "./weatherProviders/" + id ).default ) +); // Define regex filters to match against location const filters = { diff --git a/routes/weatherProviders/CompositeWeatherProvider.ts b/routes/weatherProviders/CompositeWeatherProvider.ts new file mode 100644 index 0000000..c79e496 --- /dev/null +++ b/routes/weatherProviders/CompositeWeatherProvider.ts @@ -0,0 +1,45 @@ +import { GeoCoordinates, WateringData, WeatherData, WeatherProvider } from "../../types"; + +/** + * A WeatherProvider calls other WeatherProviders until one successfully returns a value. + * This is a special utility WeatherProvider, and should NOT be selected with the WEATHER_PROVIDER environment variable. + */ +export default class CompositeWeatherProvider implements WeatherProvider { + + private readonly weatherProviders: WeatherProvider[]; + + public constructor( weatherProviders: WeatherProvider[] ) { + this.weatherProviders = weatherProviders; + } + + public async getWateringData( coordinates : GeoCoordinates ): Promise< WateringData > { + return await this.callMethod( "getWateringData", coordinates ) as WateringData; + } + + public async getWeatherData( coordinates : GeoCoordinates ): Promise< WeatherData > { + return await this.callMethod( "getWeatherData", coordinates ) as WeatherData; + } + + /** + * Calls a specified function in each WeatherProvider until one returns a non-undefined value. If the function is + * not defined for a WeatherProvider, it will be skipped. + * @param func The name of the function to call. + * @param args The arguments to pass to the function. + * @return A promise that will be resolved with the first non-undefined value returned by a WeatherProvider, or + * resolved with undefined if none of the WeatherProviders returned a value. + */ + private async callMethod( func: "getWateringData" | "getWeatherData", ...args: any ): Promise< unknown > { + for ( const weatherProvider of this.weatherProviders ) { + if ( !weatherProvider[ func ] ) { + continue; + } + + // @ts-ignore + const result = await weatherProvider[ func ]( ...args ); + if ( result ) { + return result; + } + } + return undefined; + } +}