analytics/AnalyticsBase.js

import Api from '../api/Api'
import AnalyticsRequest from './AnalyticsRequest'

/**
 * @private
 * @description
 * Base class for communicating with the analytics API endpoint.
 * Its subclasses can be used to get analytics data.
 *
 * @param {Instance} [api=<Api>] Api instance to use for the requests
 *
 * @requires module:api/Api
 *
 * @memberof module:analytics
 * @abstract
 */
class AnalyticsBase {
    constructor(api = Api.getApi()) {
        this.api = api
    }

    /**
     * Loads the analytics data and returns them as an object from the promise.
     * Two parallel requests are made against the analytics api.
     * One for getting only the metaData and one for getting the actual data.
     * This is for caching purposes, as in many situations the metaData request changes while
     * the data one will be the same and thus have the same response.
     * This methods takes care of adding the default extra parameters to both requests.
     *
     * @param {!AnalyticsRequest} req Analytics request object with the request details
     *
     * @returns {Promise} Promise that resolves with the analytics data and metaData from the api.
     *
     * @example
     * const req = new d2.analytics.request()
     *  .addDataDimension(['Uvn6LCg7dVU','OdiHJayrsKo'])
     *  .addPeriodDimension('LAST_4_QUARTERS')
     *  .addOrgUnitDimension(['lc3eMKXaEfw','PMa2VCrupOd']);
     *
     * d2.analytics.aggregate
     *  .get(req)
     *  .then(analyticsData => console.log('Analytics data', analyticsData))
     *
     * // { metaData: { ... }, rows: [ ... ], headers: [ ... ], height: 0, width: 0 }
     */
    get(req) {
        // keep metaData and data requests separate for caching purposes
        const metaDataReq = new AnalyticsRequest(req)
            .withSkipMeta(false)
            .withSkipData(true)
            .withIncludeMetadataDetails(true)

        const dataReq = new AnalyticsRequest(req)
            .withSkipData(false)
            .withSkipMeta(true)

        // parallelize requests
        return Promise.all([
            this.fetch(dataReq, { sorted: true }),
            this.fetch(metaDataReq),
        ]).then(responses =>
            Promise.resolve({
                ...responses[0],
                metaData: responses[1].metaData,
            })
        )
    }

    /**
     * @private
     * @description
     * This method does not manipulate the request object, but directly requests the data from the api
     * based on the request's configuration.
     *
     * @param {!AnalyticsRequest} req Request object
     * @param {Object} options Optional configurations, ie. for sorting dimensions
     *
     * @returns {Promise} Promise that resolves with the data from the api.
     *
     * @example
     * const req = new d2.analytics.request()
     *  .fromModel(chartModel)
     *  .withSkipData();
     *
     * d2.analytics.aggregate
     *  .fetch(req)
     *  .then(analyticsData => console.log('Analytics data', analyticsData))
     *
     * // { metaData: { ... }, rows: [], height: 0, width: 0 }
     */
    fetch(req, options) {
        return this.api
            .get(req.buildUrl(options), req.buildQuery(options))
            .then(data => Promise.resolve(data))
    }
}

export default AnalyticsBase