import { Client } from 'dynamic-yield-client'; import Config from './Contracts/Config'; import Guard from './Contracts/Guard'; import Products from './Contracts/Products'; import Recommendation from './Contracts/Recommendation'; import Subscription from './Contracts/Subscription'; import BadStatusGuard from './Guards/BadStatusGuard'; import InvalidateProductsGuard from './Guards/InvalidateProductsGuard'; import MetaDeferGuard from './Guards/MetaDeferGuard'; import MissingAssetGuard from './Guards/MissingAssetGuard'; import AssetsManager from './AssetsManager'; import Storage from './Storage'; import SubscriptionManager from './SubscriptionManager'; import Trending from './Trending'; class TrendingService { /** * * * @type {AssetsManager} */ private assets: AssetsManager; /** * * * @type {Client} */ private client: Client; /** * * * @type {Products} */ private products: Products; /** * * * @type {Storage} */ private storage: Storage; /** * * * @type {Array} */ private guards: Array = [ new BadStatusGuard(), new MetaDeferGuard(), new InvalidateProductsGuard(), new MissingAssetGuard(), ]; /** * * * @type {SubscriptionManager} */ private subscribers: SubscriptionManager = new SubscriptionManager(); /** * * * @type {number|undefined} */ private updateEventID?: number; /** * * * @param {Client} client * @param {Products} products * @param {string} daypart * @return {TrendingService} */ public constructor(client: Client, products: Products, daypart: string) { this.assets = new AssetsManager(client); this.client = client; this.products = products; this.storage = new Storage(daypart); this.guards = this.guards.map((guard) => guard.boot(products, this.storage)); } /** * * * @param {Subscription} subscription * @return {string} */ public subscribe(subscription: Subscription) { return this.subscribers.add(subscription); } /** * * * @param {string} subscriberId */ public unsubscribe(subscriberId: string) { this.subscribers.remove(subscriberId); } /** * * * @param {Config} config * @return {Promise} */ public async start(config: Config) { this.assets.update(this.products); this.update(config); } /** * * * @param {Config} config * @return {Promise} */ public async update(config: Config) { const [trending, retry] = await this.getTrending(config.slotsCount); const updateIn = retry ? config.retryInterval : config.updateInterval; if (trending) { this.storage.set(trending); } this.subscribers.notify(trending); this.updateEventID = setTimeout(() => this.update(config), updateIn); } /** * * * @param {number} slotsCount * @return {Promise<[Trending | null, boolean]>} */ public async getTrending(slotsCount: number): Promise<[Trending | null, boolean]> { try { const response = await this.client.getTrending(slotsCount); const guard = this.guards.find((guard) => ! guard.check(response)); if (guard) { return [guard.contingency(response.data), guard.retry()]; } return [new Trending(response.data, this.products), false]; } catch (error) { console.error('TrendingService - Trending fetch error.', error); return [null, true]; } } } export default TrendingService;