MitocGroup/deep-framework

View on GitHub
src/deep-cache/lib/Driver/CloudFrontDriver.js

Summary

Maintainability
A
25 mins
Test Coverage
/**
 * Created by CCristi <ccovali@mitocgroup.com> on 12/10/15.
 */

'use strict';

import {AbstractFsDriver} from './AbstractFsDriver';
import {MethodNotAvailableException} from './Exception/MethodNotAvailableException';
import {SharedKey as Key} from '../SharedKey';

/**
 * CloudFront Cache Driver
 */
export class CloudFrontDriver extends AbstractFsDriver {

  /**
   * @todo: Find better way to extract the working microservice identifier
   *
   * @param {ContainerAware} containerAware
   * @param {String} directory
   */
  constructor(containerAware, directory = AbstractFsDriver.DEFAULT_DIRECTORY) {
    super(directory);

    this._containerAware = containerAware;
    this._cache = {};
  }

  /**
   * @returns {String}
   * @private
   */
  _type() {
    return 'CloudFront';
  }

  /**
   * @param {String} key
   * @param {Function} callback
   * @private
   */
  _has(key, callback = () => {}) {
    this._get(key, (err, data) => {
      callback(err, data !== null);
    });
  }

  /**
   * @todo: Find a way to invalidate broken or expired keys
   *
   * @param {String} key
   * @param {Function} callback
   * @private
   */
  _get(key, callback = () => {}) {
    // @todo: get rid of this cache?
    if (this._cache.hasOwnProperty(key)) {
      let parsedData = this._cache[key];

      if (parsedData.expires && parsedData.expires < AbstractFsDriver._now || parsedData.buildId !== this._buildId) {
        delete this._cache[key];
      } else {
        callback(null, this._cache[key].value);
        return;
      }
    }

    this._request(key, (err, data) => {
      if (err) {
        return callback(err, null);
      }

      try {
        let parsedData = JSON.parse(data);

        if (parsedData.expires && parsedData.expires < AbstractFsDriver._now || parsedData.buildId !== this._buildId) {
          callback(null, null);

          return;
        }

        this._cache[key] = parsedData;

        callback(null, parsedData.value);
      } catch (e) { // avoid parse error on missing or broken object in S3
        callback(null, null);
      }
    });
  }

  /**
   * Throw MethodNotAvailableException
   *
   * @private
   */
  _set() {
    throw new MethodNotAvailableException('set');
  }

  /**
   * Throw MethodNotAvailableException
   *
   * @private
   */
  _invalidate() {
    throw new MethodNotAvailableException('invalidate');
  }

  /**
   * @param {String|Key} key
   * @returns {String}
   * @private
   */
  _buildKey(key) {
    return key instanceof Key ?
      this._asset.locate(`@${key.microservice}:${super._buildKey(key.toString())}`) :
      this._asset.locate(`@${this._microservice}:${super._buildKey(key)}`);
  }

  /**
   * @param {String} url
   * @param {Function} callback
   * @private
   */
  _request(url, callback) {
    var client = new XMLHttpRequest();

    client.onreadystatechange = function() {
      if (client.readyState === 4) {
        if (client.status !== 200) {
          return callback(client.statusText, null);
        }

        callback(null, client.responseText);
      }
    };

    client.open('GET', url);
    client.send();
  }

  /**
   * Returns the microservice binded to `resource` service
   *
   * @returns {Instance}
   * @private
   */
  get _microservice() {
    return this._containerAware.container.get('resource').microservice;
  }

  /**
   * @returns {Asset}
   * @private
   */
  get _asset() {
    return this._containerAware.container.get('asset');
  }
}