filipedeschamps/rss-feed-emitter

View on GitHub
src/FeedManager.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict';

/* eslint-disable class-methods-use-this */

// eslint-disable-next-line no-nested-ternary
const sortBy = (key) => (a, b) => ((a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0));

/**
 * Management class for feeds
 * @property {FeedEmitter} instance feed emitter instance to handle events
 * @property {Feed} feed feed to store configuration and items
 */
class FeedManager {
  /**
   * Manage a feed from a specific emitter
   * Side effect:
   *  - Sets the error handler for the feed.
   * @param {FeedEmitter} emitter emitter that will create events per item
   * @param {Feed} feed    feed to store items and retrieve configuration from
   */
  constructor(emitter, feed) {
    /**
     * Instance to manage
     * @type {FeedEmitter}
     */
    this.instance = emitter;
    /**
     * Feed to emit items for
     * @type {Feed}
     */
    this.feed = feed;

    this.feed.handler = {
      handle: this.onError.bind(this),
    };
  }

  /**
   * Sort all received items since we want to
   * emit them in ascending order.
   * @private
   * @param  {Object} data data to sort items on
   */
  sortItemsByDate(data) {
    data.items.sort(sortBy('date'));
  }


  /**
   * Truncated feed data fetched from web
   * @typedef {Object} FeedTrunc
   * @property {FeedItem[]} items new feed items to be added
   * @property {string} url feed url that is fetched
   */

  /**
   * Put all new items inside a "newItems" property
   * @private
   * @param  {FeedTrunc} data Data to mutate
   */
  identifyNewItems(data) {
    data.newItems = data.items.filter((fetchedItem) => {
      const foundItemInsideFeed = this.feed.findItem(fetchedItem);
      if (foundItemInsideFeed) {
        return false;
      }
      return fetchedItem;
    });
  }

  /**
   * New data to add to f a feed
   * @typedef {Object} FeedData
   * @property {Feed[]} newItems
   */

  /**
   * Now that we have all the new items, add them to the
   feed item list.
   * @private
   * @param  {FeedData} data data to mutate
   * @param {boolean} firstload Whether or not this is the first laod
   */
  populateNewItemsInFeed(data, firstload) {
    data.newItems.forEach((item) => {
      this.feed.addItem(item);
      if (!(firstload && this.instance.skipFirstLoad)) {
        this.instance.emit(this.feed.eventName, item);
      }
    });
  }

  /**
   * Handle errors during processing
   * @private
   * @param  {FeedError} error handle error
   */
  onError(error) {
    this.instance.emit('error', error);
  }

  /**
   * Get content from the managed feed
   * @public
   * @async
   * @param  {boolean}  firstload whether or not this is the first load on the manager
   */
  async getContent(firstload) {
    const items = await this.feed.fetchData();
    const data = {
      items,
      url: this.feed.url,
    };
    this.feed.updateHxLength(items);
    this.sortItemsByDate(data);
    this.identifyNewItems(data);
    this.populateNewItemsInFeed(data, firstload);
    if (firstload && !this.instance.skipFirstLoad) {
      this.instance.emit(`initial-load:${this.feed.url}`, { url: this.feed.url, items: this.feed.items });
    }
  }
}

module.exports = FeedManager;