Home Reference Source

lib/parks/plopsa/plopsabase.js

import fetch from 'node-fetch';
import {Park} from '../park.js';
import {entityType, queueType, scheduleType} from '../types.js';
import moment from 'moment-timezone';

import dotenv from 'dotenv';
dotenv.config();

/**
* Plopsa Park Object
* Make sure all environment variables are set in an .env file which should be in the main location.
* Not setting these variables will make the module exit early without returning data.
*
* This class is here to fetch the POI data and to attach queue times data to it.
* After the fetches this data is send to the end user and from there he could do whatever he wants to do.
*
* Most park specific parameters are set already
* @class
*/
export class PlopsaBase extends Park {
  /**
  * Create a new Plopsa Park object
  * @param {object} options
  */
  constructor(options = {}) {
    // Language settings
    options.cachepoistime = process.env.CACHE_DURATION_POIS;
    options.languages = process.env.LANGUAGES;

    options.langoptions = options.langoptions;

    super(options);

    // Check for existance
    if (!this.config.apiBase) throw new Error('Missing Plopsa apiBase!');
    if (!this.config.apiId) throw new Error('Missing Plopsa apiId!');
    if (!this.config.languages) {
      this.config.languages = 'en';
    };
    if (!this.config.cachepoistime) {
      this.config.cachepoistime = '12';
    };
  }

  /**
   * Login to the Plopsa API
   * @return {string} accesstoken
   */
  async loginAPI() {
    // In order to login we have to act like a mobile device
    const bodydata = {
      'clientId': '7xfwRB8iK1tbf3cYiABI%%2Fw%%3D%%3D',
      'clientSecret': '6YqyzzOsaNkxDkHmwhgK%%2Fw%%3D%%3D',
    };

    return fetch(this.config.apiBase +
      `${this.config.languages}/api/v1.0/token/0001`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(bodydata),
    },
    )
        .then((res) => res.json())
        .then((data) => {
          const token = data.accessToken;
          return token; // Return the access token and don't save it since its expiration time is almost instant
        });
  }

  /**
  * Get All POIS of Plopsa
  * This data contains all the POIS in Plopsa
  * @return {string} All Plopsa POIS
  */
  async getPOIS() {
    // Obtain the access token
    const token = await this.loginAPI();

    return fetch(this.config.apiBase +
      `${this.config.languages}/api/v1.0/details/all/${this.config.apiId}/attraction?access_token=${token}`,
    {
      method: 'GET',
    },
    )
        .then((res) => res.json())
        .then((rideData) => {
          return Promise.resolve(rideData); // Return all the attraction data immediately
        });
  }

  /**
  * Get All Rides of Plopsa
  * This data contains all the Ride POIS in Plopsa park
  * @return {string} All Plopsa ride POIS
  */
  async buildRidePOI() {
    // First fetch the ride data
    return await this.getPOIS().then((rideData) => {
      const poi = [];

      // Declare default height values
      let minHeightAccompanied = undefined;
      let minHeight = undefined;
      let maxHeight = undefined;

      Object.keys(rideData.en.attraction).forEach((ride) => { // This only fetches English data
        // Categories, however Plopsa doesn't use them
        const category = [];

        // Add the values of the height restrictions
        if (rideData.en.attraction[ride].minHeight) {
          minHeight = rideData.en.attraction[ride].minHeight;
        }
        if (rideData.en.attraction[ride].minHeightSupervised) {
          minHeightAccompanied = rideData.en.attraction[ride].minHeightSupervised;
        }
        if (rideData.en.attraction[ride].maxHeight) {
          maxHeight = rideData.en.attraction[ride].maxHeight;
        }

        // Create the restrictions Object
        const restrictions = {
          minHeightAccompanied,
          minHeight,
          maxHeight,
        };

        // Description is served, short_desc isn't
        const description = rideData.en.attraction[ride].description;
        const shortdescription = undefined;

        // Plopsaland doesn't send latlon values
        const lat = undefined;
        const lon = undefined;

        // Create the ride object
        poi[rideData.en.attraction[ride].id] = {
          name: rideData.en.attraction[ride].name,
          id: rideData.en.attraction[ride].id,
          location: {
            latitude: lat,
            longitude: lon,
          },
          meta: {
            description: description,
            shortdescription: shortdescription,
            category,
            type: entityType.ride,
            restrictions,
          },
        };
      });
      return Promise.resolve(poi); // Return all rides
    });
  }

  /**
  * Get All Queues of Plopsa
  * This data contains all the POIS in Plopsa park
  * @return {string} All Plopsa POIS with queuetimes
  */
  async getQueue() {
    // Obtain token and ridedata first
    const token = await this.loginAPI();
    const rideData = await this.buildRidePOI();

    return fetch(this.config.apiBase +
      `en/api/v1.0/waitingTime/${this.config.apiId}/attraction?access_token=${token}`,
    {
      method: 'GET',
    },
    )
        .then((res) => res.json())
        .then((waitData) => {
          // Create the ride array
          const rides = [];

          waitData.en.forEach((ride) => {
            // Declare default values (Plopsa doesn't send down or maintenance)
            let wait = ride.currentWaitingTime;
            let state = queueType.operating;
            let active = true;

            // Only includes dead and unfinished rides.
            if (wait === null) {
              wait = 0;
              state = queueType.closed;
              active = false;
            }

            // If the entity exists in the queue api and ride data, build the ride object with queues
            if (rideData[ride.id]) {
              rides.push({
                name: rideData[ride.id].name,
                id: rideData[ride.id].id,
                waitTime: wait,
                status: state,
                active: active,
                location: {
                  latitude: rideData[ride.id].location.latitude,
                  longitude: rideData[ride.id].location.longitude,
                },
                meta: {
                  description: rideData[ride.id].meta.description,
                  short_description: rideData[ride.id].meta.shortdescription,
                  category: rideData[ride.id].meta.category,
                  type: entityType.ride,
                  restrictions: rideData[ride.id].meta.restrictions,
                },
              });
            }
          });
          return rides;
        });
  }

  /**
  * Get All Operating Hours of Plopsa
  * This data contains all the Operating Hours in Plopsa, fetched with currentyear.
  * @return {string} All Plopsa calendar data
  */
  async getOpHours() {
    const token = await this.loginAPI();
    return fetch(
        this.config.apiBase +
          `en/api/v1.0/calendar/${this.config.apiId}?access_token=${token}`,
        {
          method: 'GET',
        },
    )
        .then((res) => res.json())
        .then((json) => {
          // Calendar array
          const calendar = [];

          if (!json.en) { // If the parks are closed nothing is returned at all so build an entity for today saying it's closed.
            const date = moment().format('YYYY-MM-DD');
            const time = moment('23:59', 'HH:mm a').format();
            const type = scheduleType.closed;
            const schedule = {
              name: 'Closed',
              openingTime: time,
              closingTime: time,
              type: type,
              date: date,
            };
            calendar.push(schedule);
            return calendar;
          } else {
            Object.keys(json.en.months).forEach((date) => {
              // The API only returns data if open, in seperate month objects.
              Object.keys(json.en.months[date].openOn).forEach((dates) => {
                // Create default values
                const datename = moment(dates).format('YYYY-MM-DD');
                let openTime = undefined;
                let closeTime = undefined;
                let type = undefined;
                let name = undefined;

                // Define the different opening types
                if (json.en.months[date].openOn[dates].label === '10.00 - 18.00') {
                  openTime = moment(`${datename} 10:00`).format();
                  closeTime = moment(`${datename} 18:00`).format();
                  type = scheduleType.operating;
                  name = 'Open from 10:00 - 18:00';
                } else if (json.en.months[date].openOn[dates].label === '10.00 - 17:30') {
                  openTime = moment(`${datename} 10:00`).format();
                  closeTime = moment(`${datename} 17:30`).format();
                  type = scheduleType.operating;
                  name = 'Open from 10:00 - 17:30';
                } else if (json.en.months[date].openOn[dates].label === 'Sold out') {
                  openTime = moment(`${datename} 10:00`).format();
                  closeTime = moment(`${datename} 23:00`).format(); // Longest possible closing time is 23:00+2:00
                  type = scheduleType.operating;
                  name = 'Park sold out, longest possible opening hours are in place';
                } else if (json.en.months[date].openOn[dates].label === '10.00 - 19:00') {
                  openTime = moment(`${datename} 10:00`).format();
                  closeTime = moment(`${datename} 19:00`).format();
                  type = scheduleType.operating;
                  name = 'Open from 10:00 - 19:00';
                } else if (json.en.months[date].openOn[dates].label === '10.00 - 22:30') {
                  openTime = moment(`${datename} 10:00`).format();
                  closeTime = moment(`${datename} 22:30`).format();
                  type = scheduleType.operating;
                  name = 'Open from 10:00 - 22:30';
                } else if (json.en.months[date].openOn[dates].label === 'Only Mayaland Indoor: 10.30 - 16.30') { // Only for De Panne
                  openTime = moment(`${datename} 10:30`).format();
                  closeTime = moment(`${datename} 16:30`).format();
                  type = scheduleType.operating;
                  name = 'Mayaland Indoor open from 10:30 - 16:30';
                } else if (json.en.months[date].openOn[dates].label === 'Open') { // Wtf??? Open withour hours, cool
                  openTime = moment(`${datename} 10:00`).format();
                  closeTime = moment(`${datename} 19:00`).format();
                  type = scheduleType.operating;
                  name = 'Open without hours, 10-19 hours are in place';
                } else {
                  openTime = moment(`${datename} 23:59`).format();
                  closeTime = moment(`${datename} 23:59`).format();
                  type = scheduleType.closed;
                  name = 'Closed';
                }

                // Create the schedule object
                const schedule = {
                  name: name,
                  openingTime: openTime,
                  closingTime: closeTime,
                  type: type,
                  date: datename,
                };
                calendar.push(schedule);
              });
            });
          }
          return Promise.resolve(calendar);
        });
  }
}

export default PlopsaBase;